static TypeSpecDataHolder generateBuilder()

in litho-processor/src/main/java/com/facebook/litho/specmodels/generator/BuilderGenerator.java [133:299]


  static TypeSpecDataHolder generateBuilder(SpecModel specModel) {
    final String componentName = specModel.getComponentName();
    final String componentInstanceRefName = ComponentBodyGenerator.getInstanceRefName(specModel);
    final String componentMemberInstanceName = getComponentMemberInstanceName(specModel);
    final ClassName componentClass = ClassName.bestGuess(componentName);
    final MethodSpec.Builder ctorMethodSpec =
        MethodSpec.constructorBuilder()
            .addModifiers(Modifier.PRIVATE)
            .addParameter(specModel.getContextClass(), CONTEXT_PARAM_NAME);

    final ImmutableList<PropDefaultModel> propDefaults = specModel.getPropDefaults();
    boolean isResResolvable = false;

    for (PropDefaultModel propDefault : propDefaults) {
      if (propDefault.isResResolvable()) {
        isResResolvable = true;
        break;
      }
    }

    if (specModel.isStylingSupported()) {
      ctorMethodSpec
          .addParameter(int.class, "defStyleAttr")
          .addParameter(int.class, "defStyleRes")
          .addParameter(componentClass, componentInstanceRefName)
          .addStatement("super(context, defStyleAttr, defStyleRes, $L)", componentInstanceRefName);
    } else {
      ctorMethodSpec
          .addParameter(componentClass, componentInstanceRefName)
          .addStatement("super(context, $L)", componentInstanceRefName);
    }

    ctorMethodSpec
        .addStatement("$L = $L", componentMemberInstanceName, componentInstanceRefName)
        .addStatement("$L = $L", CONTEXT_MEMBER_NAME, CONTEXT_PARAM_NAME);

    if (isResResolvable) {
      ctorMethodSpec.addStatement("initPropDefaults()");
    }

    final MethodSpec.Builder setComponentMethodSpec =
        MethodSpec.methodBuilder("setComponent")
            .addModifiers(Modifier.PROTECTED)
            .addAnnotation(Override.class)
            .addParameter(ClassNames.COMPONENT, "component")
            .addStatement(
                "$L = ($T) component",
                getComponentMemberInstanceName(specModel),
                specModel.getComponentTypeName());

    final boolean builderHasTypeVariables = !specModel.getTypeVariables().isEmpty();

    TypeName builderType = getBuilderType(specModel);

    final TypeSpec.Builder propsBuilderClassBuilder =
        TypeSpec.classBuilder(BUILDER)
            .addAnnotation(Generated.class)
            .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
            .superclass(
                ParameterizedTypeName.get(
                    ClassName.get(
                        specModel.getComponentClass().packageName(),
                        specModel.getComponentClass().simpleName(),
                        BUILDER),
                    builderType))
            .addField(componentClass, componentMemberInstanceName)
            .addField(specModel.getContextClass(), CONTEXT_MEMBER_NAME);

    if (builderHasTypeVariables) {
      propsBuilderClassBuilder.addTypeVariables(specModel.getTypeVariables());
    }

    final List<String> requiredPropNames = new ArrayList<>();
    int numRequiredProps = 0;
    for (PropModel prop : specModel.getProps()) {
      if (!prop.isOptional()) {
        numRequiredProps++;
        requiredPropNames.add(prop.getName());
      }
    }

    if (numRequiredProps > 0) {
      propsBuilderClassBuilder.addField(
          FieldSpec.builder(String[].class, REQUIRED_PROPS_NAMES, Modifier.PRIVATE, Modifier.FINAL)
              .initializer("new String[] {$L}", commaSeparateAndQuoteStrings(requiredPropNames))
              .build());

      propsBuilderClassBuilder.addField(
          FieldSpec.builder(int.class, REQUIRED_PROPS_COUNT, Modifier.PRIVATE, Modifier.FINAL)
              .initializer("$L", numRequiredProps)
              .build());

      propsBuilderClassBuilder.addField(
          FieldSpec.builder(BitSet.class, "mRequired", Modifier.PRIVATE, Modifier.FINAL)
              .initializer("new $T($L)", BitSet.class, REQUIRED_PROPS_COUNT)
              .build());

      ctorMethodSpec.addStatement("mRequired.clear()");
    }

    propsBuilderClassBuilder.addMethod(ctorMethodSpec.build());

    if (specModel instanceof LayoutSpecModel || specModel instanceof MountSpecModel) {
      propsBuilderClassBuilder.addMethod(setComponentMethodSpec.build());
    }

    if (isResResolvable) {
      MethodSpec.Builder initResTypePropDefaultsSpec = MethodSpec.methodBuilder("initPropDefaults");

      for (PropDefaultModel propDefault : propDefaults) {
        if (!propDefault.isResResolvable()) {
          continue;
        }

        initResTypePropDefaultsSpec.addStatement(
            "this.$L.$L = $L",
            getComponentMemberInstanceName(specModel),
            propDefault.getName(),
            generatePropsDefaultInitializers(specModel, propDefault));
      }

      propsBuilderClassBuilder.addMethod(initResTypePropDefaultsSpec.build());
    }

    int requiredPropIndex = 0;
    for (PropModel prop : specModel.getProps()) {
      generatePropsBuilderMethods(specModel, prop, requiredPropIndex)
          .addToTypeSpec(propsBuilderClassBuilder);

      if (!prop.isOptional()) {
        requiredPropIndex++;
      }
    }

    for (EventDeclarationModel eventDeclaration : specModel.getEventDeclarations()) {
      propsBuilderClassBuilder.addMethod(
          generateEventDeclarationBuilderMethod(specModel, eventDeclaration));
    }

    for (SpecMethodModel<EventMethod, EventDeclarationModel> triggerMethod :
        specModel.getTriggerMethods()) {
      propsBuilderClassBuilder.addMethod(
          generateEventTriggerBuilderMethod(specModel, triggerMethod));

      propsBuilderClassBuilder.addMethod(
          generateEventTriggerChangeKeyMethod(specModel, triggerMethod));
    }

    if (!specModel.getTriggerMethods().isEmpty()) {
      propsBuilderClassBuilder.addMethod(
          generateRegisterEventTriggersMethod(specModel.getTriggerMethods()));
    }

    for (BuilderMethodModel builderMethodModel : specModel.getExtraBuilderMethods()) {
      if (builderMethodModel.paramName.equals("key") && !specModel.getTriggerMethods().isEmpty()) {
        // The key setter method has been created if we have trigger methods, ignore it.
        continue;
      }
      propsBuilderClassBuilder.addMethod(generateExtraBuilderMethod(specModel, builderMethodModel));
    }

    propsBuilderClassBuilder
        .addMethod(generateGetThisMethod(specModel))
        .addMethod(generateBuildMethod(specModel, numRequiredProps));

    return TypeSpecDataHolder.newBuilder().addType(propsBuilderClassBuilder.build()).build();
  }