private emitDataType()

in packages/jsii-pacmak/lib/targets/java.ts [2408:2576]


  private emitDataType(ifc: spec.InterfaceType) {
    // collect all properties from all base structs and dedupe by name. It is assumed that the generation of the
    // assembly will not permit multiple overloaded inherited properties with the same name and that this will be
    // enforced by Typescript constraints.
    const propsByName: { [name: string]: JavaProp } = {};

    function collectProps(
      this: JavaGenerator,
      currentIfc: spec.InterfaceType,
      isBaseClass = false,
    ) {
      for (const property of currentIfc.properties ?? []) {
        const javaProp = this.toJavaProp(property, currentIfc, isBaseClass);
        propsByName[javaProp.propName] = javaProp;
      }

      // add props of base struct
      for (const base of currentIfc.interfaces ?? []) {
        collectProps.call(
          this,
          this.findType(base) as spec.InterfaceType,
          true,
        );
      }
    }

    collectProps.call(this, ifc);
    const props = Object.values(propsByName);
    this.emitInterfaceBuilder(ifc, INTERFACE_PROXY_CLASS_NAME, props);

    // Start implementation class
    this.code.line();
    this.code.line('/**');
    this.code.line(` * An implementation for {@link ${ifc.name}}`);
    this.code.line(' */');
    this.emitStabilityAnnotations(ifc);
    this.code.line(ANN_INTERNAL);
    this.code.openBlock(
      `final class ${INTERFACE_PROXY_CLASS_NAME} extends software.amazon.jsii.JsiiObject implements ${ifc.name}`,
    );

    // Immutable properties
    props.forEach((prop) =>
      this.code.line(`private final ${prop.fieldJavaType} ${prop.fieldName};`),
    );

    // Start JSII reference constructor
    this.code.line();
    this.code.line('/**');
    this.code.line(
      ' * Constructor that initializes the object based on values retrieved from the JsiiObject.',
    );
    this.code.line(' * @param objRef Reference to the JSII managed object.');
    this.code.line(' */');
    this.code.openBlock(
      `protected ${INTERFACE_PROXY_CLASS_NAME}(final software.amazon.jsii.JsiiObjectRef objRef)`,
    );
    this.code.line('super(objRef);');
    props.forEach((prop) =>
      this.code.line(
        `this.${prop.fieldName} = software.amazon.jsii.Kernel.get(this, "${prop.jsiiName}", ${prop.fieldNativeType});`,
      ),
    );
    this.code.closeBlock();
    // End JSII reference constructor

    // Start builder constructor
    this.code.line();
    this.code.line('/**');
    this.code.line(
      ' * Constructor that initializes the object based on literal property values passed by the {@link Builder}.',
    );
    this.code.line(' */');
    if (props.some((prop) => prop.fieldJavaType !== prop.paramJavaType)) {
      this.code.line('@SuppressWarnings("unchecked")');
    }
    this.code.openBlock(
      `protected ${INTERFACE_PROXY_CLASS_NAME}(final ${BUILDER_CLASS_NAME} builder)`,
    );
    this.code.line(
      'super(software.amazon.jsii.JsiiObject.InitializationMode.JSII);',
    );
    props.forEach((prop) => {
      const explicitCast =
        prop.fieldJavaType !== prop.paramJavaType
          ? `(${prop.fieldJavaType})`
          : '';
      this.code.line(
        `this.${prop.fieldName} = ${explicitCast}${_validateIfNonOptional(
          `builder.${prop.fieldName}`,
          prop,
        )};`,
      );
    });
    this.code.closeBlock();
    // End literal constructor

    // Getters
    props.forEach((prop) => {
      this.code.line();
      this.code.line('@Override');
      this.code.openBlock(
        `public final ${prop.fieldJavaType} get${prop.propName}()`,
      );
      this.code.line(`return this.${prop.fieldName};`);
      this.code.closeBlock();
    });

    // emit $jsii$toJson which will be called to serialize this object when sent to JS
    this.code.line();
    this.code.line('@Override');
    this.code.line(ANN_INTERNAL);
    this.code.openBlock(
      'public com.fasterxml.jackson.databind.JsonNode $jsii$toJson()',
    );
    this.code.line(
      'final com.fasterxml.jackson.databind.ObjectMapper om = software.amazon.jsii.JsiiObjectMapper.INSTANCE;',
    );
    this.code.line(
      `final com.fasterxml.jackson.databind.node.ObjectNode data = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode();`,
    );

    this.code.line();
    for (const prop of props) {
      if (prop.nullable) {
        this.code.openBlock(`if (this.get${prop.propName}() != null)`);
      }
      this.code.line(
        `data.set("${prop.spec.name}", om.valueToTree(this.get${prop.propName}()));`,
      );
      if (prop.nullable) {
        this.code.closeBlock();
      }
    }

    this.code.line();
    this.code.line(
      'final com.fasterxml.jackson.databind.node.ObjectNode struct = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode();',
    );
    this.code.line(`struct.set("fqn", om.valueToTree("${ifc.fqn}"));`);
    this.code.line('struct.set("data", data);');

    this.code.line();
    this.code.line(
      'final com.fasterxml.jackson.databind.node.ObjectNode obj = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode();',
    );
    this.code.line('obj.set("$jsii.struct", struct);');

    this.code.line();
    this.code.line('return obj;');
    this.code.closeBlock();
    // End $jsii$toJson

    // Generate equals() override
    this.emitEqualsOverride(ifc.name, props);

    // Generate hashCode() override
    this.emitHashCodeOverride(props);

    this.code.closeBlock();
    // End implementation class

    function _validateIfNonOptional(variable: string, prop: JavaProp): string {
      if (prop.nullable) {
        return variable;
      }
      return `java.util.Objects.requireNonNull(${variable}, "${prop.fieldName} is required")`;
    }
  }