public TypeSpec generateMessageImpl()

in modules/network-annotation-processor/src/main/java/org/apache/ignite/internal/network/processor/messages/MessageImplGenerator.java [104:321]


    public TypeSpec generateMessageImpl(MessageClass message, TypeSpec builderInterface) {
        ClassName messageImplClassName = message.implClassName();

        processingEnv.getMessager()
                .printMessage(Diagnostic.Kind.NOTE, "Generating " + messageImplClassName);

        List<ExecutableElement> getters = message.getters();

        var fields = new ArrayList<FieldSpec>(getters.size());
        var methodImpls = new ArrayList<MethodSpec>(getters.size());

        var notNullFieldNames = new HashSet<String>();
        var marshallableFieldNames = new HashSet<String>();

        // create a field and a getter implementation for every getter in the message interface
        for (ExecutableElement getter : getters) {
            TypeMirror getterType = getter.getReturnType();
            TypeName getterReturnType = TypeName.get(getterType);

            String getterName = getter.getSimpleName().toString();

            boolean isMarshallable = getter.getAnnotation(Marshallable.class) != null;

            if (isMarshallable && !marshallableTypesBlackList.canBeMarshallable(getterType)) {
                String error = String.format(
                        "\"%s\" field is marked as @Marshallable but this type is either directly supported by native serialization "
                                + "or is prohibited by a blacklist, remove this annotation from the field",
                        getterName
                );

                throw new ProcessingException(error, null, getter);
            }

            FieldSpec.Builder fieldBuilder = FieldSpec.builder(getterReturnType, getterName)
                    .addModifiers(Modifier.PRIVATE);

            if (getter.getAnnotation(IgniteToStringExclude.class) == null) {
                IgniteToStringInclude includeAnnotation = getter.getAnnotation(IgniteToStringInclude.class);
                IgniteStringifier stringifierAnnotation = getter.getAnnotation(IgniteStringifier.class);

                if (stringifierAnnotation != null) {
                    AnnotationSpec annotationSpec = AnnotationSpec.builder(IgniteStringifier.class)
                            .addMember("name", "$S", stringifierAnnotation.name())
                            .addMember("value", "$T.class", igniteStringifierValueTypeMirror(stringifierAnnotation))
                            .build();

                    fieldBuilder.addAnnotation(annotationSpec);
                } else if (includeAnnotation != null) {
                    fieldBuilder.addAnnotation(AnnotationSpec.get(includeAnnotation));
                } else {
                    fieldBuilder.addAnnotation(AnnotationSpec.builder(IgniteToStringInclude.class).build());
                }
            } else {
                fieldBuilder.addAnnotation(IgniteToStringExclude.class);
            }

            boolean generateSetter = getter.getAnnotation(WithSetter.class) != null;

            if (!isMarshallable && !generateSetter) {
                fieldBuilder.addModifiers(Modifier.FINAL);
            }

            if (requiresNotNullCheck(getter)) {
                notNullFieldNames.add(getterName);
            }

            FieldSpec field = fieldBuilder.build();
            fields.add(field);

            if (isMarshallable) {
                marshallableFieldNames.add(getterName);

                String name = addByteArrayPostfix(getterName);
                FieldSpec marshallableFieldArray = FieldSpec.builder(BYTE_ARRAY_TYPE, name)
                        .addModifiers(Modifier.PRIVATE)
                        .build();

                fields.add(marshallableFieldArray);

                MethodSpec baGetterImpl = MethodSpec.methodBuilder(name)
                        .returns(BYTE_ARRAY_TYPE)
                        .addStatement("return $N", marshallableFieldArray)
                        .build();

                methodImpls.add(baGetterImpl);
            }

            if (generateSetter) {
                MethodSpec setterImpl = MethodSpec.methodBuilder(getterName)
                        .returns(TypeName.VOID)
                        .addModifiers(Modifier.PUBLIC)
                        .addParameter(getterReturnType, getterName)
                        .addAnnotation(Override.class)
                        .addStatement("this.$L = $L", getterName, getterName)
                        .build();

                methodImpls.add(setterImpl);
            }

            MethodSpec getterImpl = MethodSpec.overriding(getter)
                    .addStatement("return $N", field)
                    .build();

            methodImpls.add(getterImpl);
        }

        TypeSpec.Builder messageImpl = TypeSpec.classBuilder(messageImplClassName)
                .addModifiers(Modifier.PUBLIC)
                .addSuperinterface(message.className())
                .addSuperinterface(Cloneable.class)
                .addFields(fields)
                .addMethods(methodImpls)
                .addMethod(constructor(fields, notNullFieldNames, marshallableFieldNames));

        if (message.isAutoSerializable()) {
            messageImpl.addMethod(MethodSpec.methodBuilder("serializer")
                    .returns(MessageSerializer.class)
                    .addModifiers(Modifier.PUBLIC)
                    .addAnnotation(Override.class)
                    .addCode("return $T.INSTANCE;", message.serializerClassName())
                    .build());
        } else {
            messageImpl.addMethod(MethodSpec.methodBuilder("serializer")
                    .returns(MessageSerializer.class)
                    .addModifiers(Modifier.PUBLIC)
                    .addAnnotation(Override.class)
                    .addCode("return null;")
                    .build());
        }

        // group type constant and getter
        FieldSpec groupTypeField = FieldSpec.builder(short.class, "GROUP_TYPE")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
                .initializer("$L", messageGroup.groupType())
                .build();

        messageImpl.addField(groupTypeField);

        MethodSpec groupTypeMethod = MethodSpec.methodBuilder("groupType")
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .returns(short.class)
                .addStatement("return $N", groupTypeField)
                .build();

        messageImpl.addMethod(groupTypeMethod);

        MethodSpec toStringMethod = MethodSpec.methodBuilder("toString")
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .returns(String.class)
                .addStatement("return $T.toString($T.class, this)", S.class, messageImplClassName)
                .build();

        messageImpl.addMethod(toStringMethod);

        // message type constant and getter
        FieldSpec messageTypeField = FieldSpec.builder(short.class, "TYPE")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
                .initializer("$L", message.messageType())
                .build();

        messageImpl.addField(messageTypeField);

        MethodSpec messageTypeMethod = MethodSpec.methodBuilder("messageType")
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .returns(short.class)
                .addStatement("return $N", messageTypeField)
                .build();

        messageImpl.addMethod(messageTypeMethod);

        // equals and hashCode
        generateEqualsAndHashCode(messageImpl, message);

        // generate clone
        MethodSpec cloneMethod = MethodSpec.methodBuilder("clone")
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .returns(messageImplClassName)
                .addCode(CodeBlock.builder()
                        .beginControlFlow("try")
                        .addStatement("return ($T) super.clone()", messageImplClassName)
                        .endControlFlow()
                        .beginControlFlow("catch (CloneNotSupportedException e)")
                        .addStatement("// Never expected to be thrown because whole message class hierarchy implements clone()")
                        .addStatement("throw new AssertionError(e)")
                        .endControlFlow()
                        .build())
                .build();

        messageImpl.addMethod(cloneMethod);

        var builderName = ClassName.get(message.packageName(), builderInterface.name);

        // nested builder interface and static factory method
        TypeSpec builder = generateBuilderImpl(message, messageImplClassName, builderName, notNullFieldNames, marshallableFieldNames);

        messageImpl.addType(builder);

        MethodSpec builderMethod = MethodSpec.methodBuilder("builder")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(builderName)
                .addStatement("return new $N()", builder)
                .build();

        messageImpl.addMethod(builderMethod);

        generatePrepareMarshal(messageImpl, message);
        generateUnmarshalMethod(messageImpl, message);

        messageImpl
                .addOriginatingElement(message.element())
                .addOriginatingElement(messageGroup.element());

        return messageImpl.build();
    }