io.soabase.halva.processor.caseclass.Templates.java Source code

Java tutorial

Introduction

Here is the source code for io.soabase.halva.processor.caseclass.Templates.java

Source

/**
 * Copyright 2016 Jordan Zimmerman
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.soabase.halva.processor.caseclass;

import com.squareup.javapoet.*;
import io.soabase.halva.any.Any;
import io.soabase.halva.any.AnyClassTuple;
import io.soabase.halva.any.AnyVal;
import io.soabase.halva.processor.Environment;
import io.soabase.halva.tuple.Tuple;
import io.soabase.halva.tuple.details.Tuple0;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

class Templates {
    private final Initializers initializers;
    private final Environment environment;

    Templates(Environment environment) {
        initializers = new Initializers(environment);
        this.environment = environment;
    }

    void addField(CaseClassItem item, TypeSpec.Builder builder, TypeName type, boolean makeFinal,
            boolean makeVolatile, Settings settings) {
        TypeName localType;
        if (makeFinal) {
            localType = type;
        } else {
            localType = item.hasDefaultValue() ? type.box() : type;
        }
        FieldSpec.Builder fieldBuilder = FieldSpec.builder(localType, item.getName(), Modifier.PRIVATE);
        if (makeFinal) {
            fieldBuilder.addModifiers(Modifier.FINAL);
        }
        if (makeVolatile) {
            fieldBuilder.addModifiers(Modifier.VOLATILE);
        }
        Set<AnnotationSpec> annotationSpecs = new HashSet<>();
        if (settings.json) {
            List<AnnotationSpec> parent = parentJsonAnnotations(item.getElement());
            if (parent.isEmpty()) {
                AnnotationSpec annotationSpec = AnnotationSpec
                        .builder(ClassName.get("com.fasterxml.jackson.annotation", "JsonProperty")).build();
                parent = Collections.singletonList(annotationSpec);
            }
            annotationSpecs.addAll(parent);
        }
        if (settings.validate) {
            item.getElement().getAnnotationMirrors().stream().map(AnnotationSpec::get)
                    .forEach(annotationSpecs::add);
        }
        fieldBuilder.addAnnotations(annotationSpecs);
        builder.addField(fieldBuilder.build());
    }

    void addGetter(CaseClassItem item, TypeSpec.Builder builder, TypeName type, Modifier... modifiers) {
        MethodSpec methodSpec = MethodSpec.methodBuilder(item.getName()).returns(type).addModifiers(modifiers)
                .addAnnotation(Override.class).addStatement("return $L", item.getName()).build();

        builder.addMethod(methodSpec);
    }

    void addSetter(CaseClassItem item, TypeSpec.Builder builder, TypeName type, Modifier... modifiers) {
        ParameterSpec parameterSpec = ParameterSpec.builder(type, item.getName()).build();

        MethodSpec methodSpec = MethodSpec.methodBuilder(item.getName()).returns(TypeName.VOID)
                .addModifiers(modifiers).addParameter(parameterSpec)
                .addStatement("this.$L = $L", item.getName(), item.getName()).build();

        builder.addMethod(methodSpec);
    }

    void addBuilderSetter(CaseClassItem item, TypeSpec.Builder builder, TypeName type, TypeName builderClassName,
            Modifier... modifiers) {
        ParameterSpec parameterSpec = ParameterSpec.builder(type, item.getName()).build();

        MethodSpec methodSpec = MethodSpec.methodBuilder(item.getName()).returns(builderClassName)
                .addModifiers(modifiers).addParameter(parameterSpec)
                .addStatement("this.$L = $L", item.getName(), item.getName()).addStatement("return this").build();

        builder.addMethod(methodSpec);
    }

    void addGetterItem(CaseClassItem item, TypeSpec.Builder builder, Settings settings) {
        TypeName type = environment.getGeneratedManager().toTypeName(item.getType());
        addField(item, builder, type, true, false, settings);
        addGetter(item, builder, type, Modifier.PUBLIC);
    }

    void addSetterItem(CaseClassItem item, TypeSpec.Builder builder, Settings settings) {
        TypeName type = environment.getGeneratedManager().toTypeName(item.getType());
        addField(item, builder, type, false, true, settings);
        addGetter(item, builder, type, Modifier.PUBLIC);
        addSetter(item, builder, type, Modifier.PUBLIC);
    }

    void addItem(CaseClassItem item, TypeSpec.Builder builder, Settings settings) {
        if (item.isMutable()) {
            addSetterItem(item, builder, settings);
        } else {
            addGetterItem(item, builder, settings);
        }
    }

    void addBuilderSetterItem(CaseClassItem item, TypeSpec.Builder builder, TypeName builderClassName,
            Settings settings) {
        TypeName type = environment.getGeneratedManager().toTypeName(item.getType());
        addField(item, builder, type, false, false, settings);
        addBuilderSetter(item, builder, type, builderClassName, Modifier.PUBLIC);
    }

    void addConstructor(CaseClassSpec spec, TypeSpec.Builder builder) {
        MethodSpec.Builder constructor = MethodSpec.constructorBuilder().addCode(buildFieldValidation(spec))
                .addModifiers(Modifier.PROTECTED);
        spec.getItems().stream().forEach(item -> {
            TypeName type = environment.getGeneratedManager().toTypeName(item.getType());
            constructor.addParameter(item.hasDefaultValue() ? type.box() : type, item.getName());
            constructor.addStatement("this.$L = $L", item.getName(), item.getName());
        });
        builder.addMethod(constructor.build());
    }

    void addTuple(CaseClassSpec spec, TypeSpec.Builder builder) {
        Optional<Class<? extends Tuple>> optionalTupleClass = Tuple.getTupleClass(spec.getItems().size());
        boolean hasThisTuple = optionalTupleClass.isPresent();
        Class<? extends Tuple> tupleClass = optionalTupleClass.orElse(Tuple.class);

        TypeName typeName;
        CodeBlock codeBlock;
        if (hasThisTuple) {
            List<TypeName> typeNameList = spec.getItems().stream()
                    .map(item -> environment.getGeneratedManager().toTypeName(item.getType()).box())
                    .collect(Collectors.toList());
            typeName = getTupleType(tupleClass, typeNameList);

            String args = spec.getItems().stream().map(item -> item.getName() + "()")
                    .collect(Collectors.joining(", "));

            codeBlock = CodeBlock.builder().addStatement("return $T.Tu($L)", Tuple.class, args).build();
        } else {
            typeName = ClassName.get(tupleClass);

            codeBlock = CodeBlock.builder().addStatement("throw new $T($S)", UnsupportedOperationException.class,
                    "Too many arguments for a Tuple").build();
        }

        MethodSpec methodSpec = MethodSpec.methodBuilder("tuple").returns(typeName).addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC).addCode(codeBlock).build();
        builder.addMethod(methodSpec);
    }

    void addHashCode(CaseClassSpec spec, TypeSpec.Builder builder) {
        MethodSpec.Builder hashCodeBuilder = MethodSpec.methodBuilder("hashCode").returns(TypeName.INT)
                .addAnnotation(Override.class).addModifiers(Modifier.PUBLIC);
        boolean firstTime = true;
        for (CaseClassItem item : spec.getItems()) {
            String field;
            if (item.getType().getKind() == TypeKind.BOOLEAN) {
                field = "(" + item.getName() + " ? 1 : 0)";
            } else if (item.getType().getKind() == TypeKind.FLOAT) {
                field = "Float.hashCode(" + item.getName() + ")";
            } else if (item.getType().getKind() == TypeKind.DOUBLE) {
                field = "Double.hashCode(" + item.getName() + ")";
            } else if (item.getType().getKind() == TypeKind.LONG) {
                field = "Long.hashCode(" + item.getName() + ")";
            } else if (item.getType().getKind().isPrimitive()) {
                field = item.getName();
            } else {
                field = item.getName() + ".hashCode()";
            }
            String format = firstTime ? "int result = $L" : "result = 31 * result + $L";
            hashCodeBuilder.addStatement(format, field);
            firstTime = false;
        }
        if (spec.getItems().size() > 0) {
            hashCodeBuilder.addStatement("return result");
        } else {
            hashCodeBuilder.addStatement("return super.hashCode()");
        }
        builder.addMethod(hashCodeBuilder.build());
    }

    void addEquals(CaseClassSpec spec, TypeSpec.Builder builder, ClassName className) {
        MethodSpec.Builder equalsBuilder = MethodSpec.methodBuilder("equals").returns(TypeName.BOOLEAN)
                .addParameter(ParameterSpec.builder(Object.class, "rhsObj").build()).addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC);

        equalsBuilder.beginControlFlow("if ( this == rhsObj )").addStatement("return true").endControlFlow();

        equalsBuilder.beginControlFlow("if ( rhsObj == null || getClass() != rhsObj.getClass() )")
                .addStatement("return false").endControlFlow();

        if (spec.getItems().size() > 0) {
            equalsBuilder.addStatement("$L rhs = ($L)rhsObj", className.simpleName(), className.simpleName());
        }

        spec.getItems().forEach(item -> {
            if (item.getType().getKind().isPrimitive()) {
                equalsBuilder.beginControlFlow("if ( $L != rhs.$L )", item.getName(), item.getName())
                        .addStatement("return false").endControlFlow();
            } else {
                equalsBuilder.beginControlFlow("if ( !$L.equals(rhs.$L) )", item.getName(), item.getName())
                        .addStatement("return false").endControlFlow();
            }
        });
        equalsBuilder.addStatement("return true");
        builder.addMethod(equalsBuilder.build());
    }

    void addDebugString(CaseClassSpec spec, TypeSpec.Builder builder, ClassName className) {
        MethodSpec.Builder toStringBuilder = MethodSpec.methodBuilder("debugString").returns(String.class)
                .addModifiers(Modifier.PUBLIC);

        toStringBuilder.addCode("return \"$L { \" +\n", className.simpleName());
        spec.getItems().forEach(item -> toDebugStringItem(toStringBuilder, item));
        toStringBuilder.addStatement("'}'");
        builder.addMethod(toStringBuilder.build());
    }

    void addToString(CaseClassSpec spec, TypeSpec.Builder builder, ClassName className) {
        MethodSpec.Builder toStringBuilder = MethodSpec.methodBuilder("toString").returns(String.class)
                .addAnnotation(Override.class).addModifiers(Modifier.PUBLIC);

        toStringBuilder.addCode("return \"$L(\" +\n", className.simpleName());
        boolean isFirst = true;
        for (CaseClassItem item : spec.getItems()) {
            toStringItem(toStringBuilder, item, isFirst);
            isFirst = false;
        }
        toStringBuilder.addStatement("')'");
        builder.addMethod(toStringBuilder.build());
    }

    void addObjectInstance(TypeSpec.Builder builder, ClassName className,
            Optional<List<TypeVariableName>> typeVariableNames) {
        TypeName localCaseClassName = getLocalCaseClassName(className, typeVariableNames);
        FieldSpec.Builder fieldSpecBuilder = FieldSpec
                .builder(className, className.simpleName(), Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
                .initializer("new $T()", localCaseClassName);
        builder.addField(fieldSpecBuilder.build());
    }

    void addApplyBuilder(CaseClassSpec spec, TypeSpec.Builder builder, ClassName className,
            Optional<List<TypeVariableName>> typeVariableNames) {
        TypeName localCaseClassName = getLocalCaseClassName(className, typeVariableNames);

        List<ParameterSpec> parameters = spec.getItems().stream()
                .map(item -> ParameterSpec
                        .builder(environment.getGeneratedManager().toTypeName(item.getType()), item.getName())
                        .build())
                .collect(Collectors.toList());

        String arguments = spec.getItems().stream().map(CaseClassItem::getName).collect(Collectors.joining(", "));
        CodeBlock.Builder codeBlockBuilder = CodeBlock.builder().addStatement("return new $L$L($L)",
                className.simpleName(), getDuck(typeVariableNames), arguments);

        MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(className.simpleName())
                .returns(localCaseClassName).addParameters(parameters).addCode(codeBlockBuilder.build())
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC);
        if (typeVariableNames.isPresent()) {
            methodSpecBuilder.addTypeVariables(typeVariableNames.get());
        }
        builder.addMethod(methodSpecBuilder.build());
    }

    void addBuilder(CaseClassSpec spec, TypeSpec.Builder builder, ClassName className, Settings settings,
            Optional<List<TypeVariableName>> typeVariableNames) {
        TypeName builderClassName = getBuilderClassName(className, typeVariableNames);
        TypeSpec typeSpec = buildBuilderClass(spec, className, builderClassName, settings, typeVariableNames);
        MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("builder").returns(builderClassName)
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addStatement("return new Builder$L()", getDuck(typeVariableNames));

        if (typeVariableNames.isPresent()) {
            methodSpecBuilder.addTypeVariables(typeVariableNames.get());
        }

        builder.addMethod(methodSpecBuilder.build());
        builder.addType(typeSpec);
    }

    TypeName getBuilderClassName(ClassName className, Optional<List<TypeVariableName>> typeVariableNames) {
        ClassName rawClassname = className.nestedClass("Builder");
        if (typeVariableNames.isPresent()) {
            return ParameterizedTypeName.get(rawClassname,
                    typeVariableNames.get().toArray(new TypeName[typeVariableNames.get().size()]));
        }
        return rawClassname;
    }

    void addCopy(TypeSpec.Builder builder, ClassName className,
            Optional<List<TypeVariableName>> typeVariableNames) {
        TypeName builderClassName = getBuilderClassName(className, typeVariableNames);
        MethodSpec copySpec = MethodSpec
                .methodBuilder("copy").returns(builderClassName).addCode(CodeBlock.builder()
                        .addStatement("return new Builder$L(this)", getDuck(typeVariableNames)).build())
                .addModifiers(Modifier.PUBLIC).build();
        builder.addMethod(copySpec);
    }

    void addClassTuple(CaseClassSpec spec, TypeSpec.Builder builder, ClassName className, Settings settings,
            Optional<List<TypeVariableName>> typeVariableNames) {
        addClassTupleMethods(spec, builder, className, typeVariableNames);
        addClassTuplable(spec, builder, className, settings);
    }

    private void addClassTupleMethods(CaseClassSpec spec, TypeSpec.Builder builder, ClassName className,
            Optional<List<TypeVariableName>> typeVariableNames) {
        Optional<Class<? extends Tuple>> optionalTupleClass = Tuple.getTupleClass(spec.getItems().size());
        if (!optionalTupleClass.isPresent()) {
            return;
        }

        ClassName anyClassName = ClassName.get(Any.class);
        ClassName matchClassName = ClassName.get(AnyVal.class);
        ClassName anyClassTupleName = ClassName.get(AnyClassTuple.class);
        TypeName localCaseClassName = getLocalCaseClassName(className, typeVariableNames);
        TypeName classTupleClassName = ParameterizedTypeName.get(anyClassTupleName, localCaseClassName);

        CodeBlock.Builder returnCode = CodeBlock.builder().add("return new $T($T.Tu(", classTupleClassName,
                Tuple.class);
        IntStream.range(0, spec.getItems().size()).forEach(i -> {
            CaseClassItem item = spec.getItems().get(i);
            if (i > 0) {
                returnCode.add(", ");
            }
            returnCode.add("$T.loose($L)", anyClassName, item.getName());
        });
        spec.getItems().forEach(item -> {
        });
        returnCode.addStatement(")){}");

        MethodSpec.Builder tupleMethod = MethodSpec.methodBuilder(getClassTupleMethodName(className))
                .returns(classTupleClassName).addCode(returnCode.build())
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC);
        spec.getItems().forEach(item -> {
            TypeName mainType = null;
            if (item.getType().getKind() == TypeKind.DECLARED) {
                DeclaredType declaredType = (DeclaredType) item.getType();
                List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
                if (typeArguments.size() > 0) {
                    TypeName[] typeNames = new TypeName[typeArguments.size()];
                    for (int i = 0; i < typeArguments.size(); ++i) {
                        typeNames[i] = WildcardTypeName.subtypeOf(TypeName.get(typeArguments.get(i)).box());
                    }
                    mainType = ParameterizedTypeName.get(ClassName.get((TypeElement) declaredType.asElement()),
                            typeNames);
                }
            }
            if (mainType == null) {
                mainType = TypeName.get(item.getType()).box();
            }
            TypeName wildcareType = WildcardTypeName.subtypeOf(mainType);
            ParameterizedTypeName type = ParameterizedTypeName.get(matchClassName, wildcareType);
            tupleMethod.addParameter(type, item.getName());
        });

        if (typeVariableNames.isPresent()) {
            tupleMethod.addTypeVariables(typeVariableNames.get());
        }

        builder.addMethod(tupleMethod.build());
    }

    private String getClassTupleMethodName(ClassName className) {
        return className.simpleName() + "Any";
    }

    private void addClassTuplable(CaseClassSpec spec, TypeSpec.Builder builder, ClassName className,
            Settings settings) {
        CodeBlock anyVal = CodeBlock.of("$T.any()", Any.class);
        CodeBlock.Builder initialize = CodeBlock.builder().add("$L(", getClassTupleMethodName(className));
        IntStream.range(0, spec.getItems().size()).forEach(i -> {
            if (i > 0) {
                initialize.add(", ");
            }
            initialize.add(anyVal);
        });
        initialize.add(").getClass()");
        FieldSpec.Builder fieldSpecBuilder = FieldSpec
                .builder(Class.class, "classTuplableClass", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
                .initializer(initialize.build());

        MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("getClassTuplableClass")
                .addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).returns(Class.class)
                .addCode(CodeBlock.builder().addStatement("return classTuplableClass").build());
        if (settings.json) {
            methodSpecBuilder.addAnnotation(ClassName.get("com.fasterxml.jackson.annotation", "JsonIgnore"));
        }
        builder.addMethod(methodSpecBuilder.build());
        builder.addField(fieldSpecBuilder.build());
    }

    private void toStringItem(MethodSpec.Builder toStringBuilder, CaseClassItem item, boolean isFirst) {
        if (item.getType().toString().equals(String.class.getName())) {
            toStringBuilder.addCode("\"$L\\\"\" + $L + \"\\\"\" + \n", isFirst ? "" : ", ", item.getName());
        } else {
            toStringBuilder.addCode("$L$L +\n", isFirst ? "" : "\", \" + ", item.getName());
        }
    }

    private void toDebugStringItem(MethodSpec.Builder toStringBuilder, CaseClassItem item) {
        if (item.getType().toString().equals(String.class.getName())) {
            toStringBuilder.addCode("    \"$L=\\\"\" + $L + \"\\\"; \" +\n", item.getName(), item.getName());
        } else {
            toStringBuilder.addCode("    \"$L=\" + $L + \"; \" +\n", item.getName(), item.getName());
        }
    }

    private TypeName getTupleType(Class<? extends Tuple> tupleClass, List<TypeName> itemTypes) {
        TypeName tupleType;
        if (Tuple0.class.isAssignableFrom(tupleClass)) {
            tupleType = ClassName.get(Tuple0.class);
        } else {
            tupleType = ParameterizedTypeName.get(ClassName.get(tupleClass),
                    itemTypes.toArray(new TypeName[itemTypes.size()]));
        }
        return tupleType;
    }

    private String getDuck(Optional<List<TypeVariableName>> typeVariableNames) {
        return typeVariableNames.isPresent() ? "<>" : "";
    }

    private CodeBlock buildFieldValidation(CaseClassSpec spec) {
        CodeBlock.Builder codeBuilder = CodeBlock.builder();
        spec.getItems().forEach(item -> initializers.addTo(codeBuilder, spec, item));
        return codeBuilder.build();
    }

    private TypeSpec buildBuilderClass(CaseClassSpec spec, ClassName caseClassName, TypeName builderClassName,
            Settings settings, Optional<List<TypeVariableName>> typeVariableNames) {
        TypeName localCaseClassName = getLocalCaseClassName(caseClassName, typeVariableNames);
        CodeBlock.Builder newBuilder = CodeBlock.builder().add("return new $L$L(", caseClassName.simpleName(),
                getDuck(typeVariableNames));
        String comma = "";
        for (CaseClassItem item : spec.getItems()) {
            newBuilder.add("$L\n    $L", comma, item.getName());
            comma = ", ";
        }
        newBuilder.add("\n);\n");

        MethodSpec.Builder newSpecBuilder = MethodSpec.methodBuilder("build").returns(localCaseClassName)
                .addModifiers(Modifier.PUBLIC).addCode(newBuilder.build());

        MethodSpec.Builder constructorSpecBuilder = MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE);
        if (settings.json) {
            AnnotationSpec annotationSpec = AnnotationSpec
                    .builder(ClassName.get("com.fasterxml.jackson.annotation", "JsonCreator")).build();
            constructorSpecBuilder.addAnnotation(annotationSpec);
        }

        MethodSpec.Builder copyConstructorBuilder = MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE)
                .addParameter(ParameterSpec.builder(localCaseClassName, "rhs").build());
        spec.getItems().forEach(
                item -> copyConstructorBuilder.addStatement("$L = rhs.$L", item.getName(), item.getName()));

        TypeSpec.Builder builder = TypeSpec.classBuilder("Builder")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
                .addMethod(constructorSpecBuilder.build()).addMethod(copyConstructorBuilder.build())
                .addMethod(newSpecBuilder.build());
        if (settings.json) {
            AnnotationSpec annotationSpec = AnnotationSpec
                    .builder(ClassName.get("com.fasterxml.jackson.databind.annotation", "JsonPOJOBuilder"))
                    .addMember("withPrefix", "\"\"").build();
            builder.addAnnotation(annotationSpec);
        }
        if (typeVariableNames.isPresent()) {
            builder.addTypeVariables(typeVariableNames.get());
        }

        spec.getItems().forEach(item -> addBuilderSetterItem(item, builder, builderClassName, settings));

        return builder.build();
    }

    private TypeName getLocalCaseClassName(ClassName caseClassName,
            Optional<List<TypeVariableName>> typeVariableNames) {
        TypeName localCaseClassName;
        if (typeVariableNames.isPresent()) {
            localCaseClassName = ParameterizedTypeName.get(caseClassName,
                    typeVariableNames.get().toArray(new TypeName[typeVariableNames.get().size()]));
        } else {
            localCaseClassName = caseClassName;
        }
        return localCaseClassName;
    }

    private List<AnnotationSpec> parentJsonAnnotations(Element element) {
        return element.getAnnotationMirrors().stream().filter(annotation -> {
            String type = annotation.getAnnotationType().asElement().toString();
            return (type.equals("com.fasterxml.jackson.annotation.JsonProperty")
                    || type.equals("com.fasterxml.jackson.annotation.JsonIgnore"));
        }).map(annotation -> {
            AnnotationSpec.Builder annotationSpec = AnnotationSpec
                    .builder(ClassName.get((TypeElement) annotation.getAnnotationType().asElement()));
            annotation.getElementValues().entrySet().forEach(entry -> annotationSpec
                    .addMember(entry.getKey().getSimpleName().toString(), "$S", entry.getValue().getValue()));
            return annotationSpec.build();
        }).collect(Collectors.toList());
    }
}