Example usage for org.eclipse.jdt.internal.compiler.ast QualifiedTypeReference getLastToken

List of usage examples for org.eclipse.jdt.internal.compiler.ast QualifiedTypeReference getLastToken

Introduction

In this page you can find the example usage for org.eclipse.jdt.internal.compiler.ast QualifiedTypeReference getLastToken.

Prototype

@Override
    public char[] getLastToken() 

Source Link

Usage

From source file:lombok.eclipse.handlers.HandleSuperBuilder.java

License:Open Source License

@Override
public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, EclipseNode annotationNode) {
    handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder");

    long p = (long) ast.sourceStart << 32 | ast.sourceEnd;

    SuperBuilder superbuilderAnnotation = annotation.getInstance();

    String builderMethodName = superbuilderAnnotation.builderMethodName();
    String buildMethodName = superbuilderAnnotation.buildMethodName();

    if (builderMethodName == null)
        builderMethodName = "builder";
    if (buildMethodName == null)
        buildMethodName = "build";

    boolean generateBuilderMethod;
    if (builderMethodName.isEmpty()) {
        generateBuilderMethod = false;/* w w w.ja  v  a  2  s .  c o  m*/
    } else if (!checkName("builderMethodName", builderMethodName, annotationNode)) {
        return;
    } else {
        generateBuilderMethod = true;
    }
    if (!checkName("buildMethodName", buildMethodName, annotationNode))
        return;

    boolean toBuilder = superbuilderAnnotation.toBuilder();

    EclipseNode tdParent = annotationNode.up();

    java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
    TypeReference returnType;
    TypeParameter[] typeParams;

    boolean addCleaning = false;

    if (!(tdParent.get() instanceof TypeDeclaration)) {
        annotationNode.addError("@SuperBuilder is only supported on types.");
        return;
    }
    TypeDeclaration td = (TypeDeclaration) tdParent.get();

    // Gather all fields of the class that should be set by the builder.
    List<EclipseNode> allFields = new ArrayList<EclipseNode>();
    List<EclipseNode> nonFinalNonDefaultedFields = null;

    boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent)
            || hasAnnotation("lombok.experimental.Value", tdParent));
    for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) {
        FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
        EclipseNode isDefault = findAnnotation(Builder.Default.class, fieldNode);
        boolean isFinal = ((fd.modifiers & ClassFileConstants.AccFinal) != 0)
                || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode));

        Annotation[] copyableAnnotations = findCopyableAnnotations(fieldNode);

        BuilderFieldData bfd = new BuilderFieldData();
        bfd.rawName = fieldNode.getName().toCharArray();
        bfd.name = removePrefixFromField(fieldNode);
        bfd.annotations = copyAnnotations(fd, copyableAnnotations);
        bfd.type = fd.type;
        bfd.singularData = getSingularData(fieldNode, ast);
        bfd.originalFieldNode = fieldNode;

        if (bfd.singularData != null && isDefault != null) {
            isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
            isDefault = null;
        }

        if (fd.initialization == null && isDefault != null) {
            isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;').");
            isDefault = null;
        }

        if (fd.initialization != null && isDefault == null) {
            if (isFinal)
                continue;
            if (nonFinalNonDefaultedFields == null)
                nonFinalNonDefaultedFields = new ArrayList<EclipseNode>();
            nonFinalNonDefaultedFields.add(fieldNode);
        }

        if (isDefault != null) {
            bfd.nameOfDefaultProvider = prefixWith(DEFAULT_PREFIX, bfd.name);
            bfd.nameOfSetFlag = prefixWith(bfd.name, SET_PREFIX);

            MethodDeclaration md = HandleBuilder.generateDefaultProvider(bfd.nameOfDefaultProvider,
                    td.typeParameters, fieldNode, ast);
            if (md != null)
                injectMethod(tdParent, md);
        }
        addObtainVia(bfd, fieldNode);
        builderFields.add(bfd);
        allFields.add(fieldNode);
    }

    // Set the names of the builder classes.
    String builderClassName = String.valueOf(td.name) + "Builder";
    String builderImplClassName = builderClassName + "Impl";

    typeParams = td.typeParameters != null ? td.typeParameters : new TypeParameter[0];
    returnType = namePlusTypeParamsToTypeReference(td.name, typeParams, p);

    // <C, B> are the generics for our builder.
    String classGenericName = "C";
    String builderGenericName = "B";
    // If these generics' names collide with any generics on the annotated class, modify them.
    // For instance, if there are generics <B, B2, C> on the annotated class, use "C2" and "B3" for our builder.
    java.util.List<String> typeParamStrings = new ArrayList<String>();
    for (TypeParameter typeParam : typeParams)
        typeParamStrings.add(typeParam.toString());
    classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings);
    builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings);

    TypeReference extendsClause = td.superclass;
    TypeReference superclassBuilderClass = null;
    TypeReference[] typeArguments = new TypeReference[] {
            new SingleTypeReference(classGenericName.toCharArray(), 0),
            new SingleTypeReference(builderGenericName.toCharArray(), 0) };
    if (extendsClause instanceof QualifiedTypeReference) {
        QualifiedTypeReference qualifiedTypeReference = (QualifiedTypeReference) extendsClause;
        String superclassClassName = String.valueOf(qualifiedTypeReference.getLastToken());
        String superclassBuilderClassName = superclassClassName + "Builder";

        char[][] tokens = Arrays.copyOf(qualifiedTypeReference.tokens,
                qualifiedTypeReference.tokens.length + 1);
        tokens[tokens.length] = superclassBuilderClassName.toCharArray();
        long[] poss = new long[tokens.length];
        Arrays.fill(poss, p);

        TypeReference[] superclassTypeArgs = getTypeParametersFrom(extendsClause);

        // Every token may potentially have type args. Here, we only have
        // type args for the last token, the superclass' builder.
        TypeReference[][] typeArgsForTokens = new TypeReference[tokens.length][];
        typeArgsForTokens[typeArgsForTokens.length - 1] = mergeTypeReferences(superclassTypeArgs,
                typeArguments);

        superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss);
    } else if (extendsClause != null) {
        String superClass = String.valueOf(extendsClause.getTypeName()[0]);
        String superclassBuilderClassName = superClass + "Builder";

        char[][] tokens = new char[][] { superClass.toCharArray(), superclassBuilderClassName.toCharArray() };
        long[] poss = new long[tokens.length];
        Arrays.fill(poss, p);

        TypeReference[] superclassTypeArgs = getTypeParametersFrom(extendsClause);

        // Every token may potentially have type args. Here, we only have
        // type args for the last token, the superclass' builder.
        TypeReference[][] typeArgsForTokens = new TypeReference[tokens.length][];
        typeArgsForTokens[typeArgsForTokens.length - 1] = mergeTypeReferences(superclassTypeArgs,
                typeArguments);

        superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss);
    }
    // If there is no superclass, superclassBuilderClassExpression is still == null at this point.
    // You can use it to check whether to inherit or not.

    generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName,
            superclassBuilderClass != null);

    // Create the abstract builder class, or reuse an existing one.
    EclipseNode builderType = findInnerClass(tdParent, builderClassName);
    if (builderType == null) {
        builderType = generateBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass,
                typeParams, ast, classGenericName, builderGenericName);
    } else {
        TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get();
        if ((builderTypeDeclaration.modifiers
                & (ClassFileConstants.AccStatic | ClassFileConstants.AccAbstract)) == 0) {
            annotationNode.addError("Existing Builder must be an abstract static inner class.");
            return;
        }
        sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
        // Generate errors for @Singular BFDs that have one already defined node.
        for (BuilderFieldData bfd : builderFields) {
            SingularData sd = bfd.singularData;
            if (sd == null)
                continue;
            EclipseSingularizer singularizer = sd.getSingularizer();
            if (singularizer == null)
                continue;
            if (singularizer.checkForAlreadyExistingNodesAndGenerateError(builderType, sd)) {
                bfd.singularData = null;
            }
        }
    }

    // Check validity of @ObtainVia fields, and add check if adding cleaning for @Singular is necessary.
    for (BuilderFieldData bfd : builderFields) {
        if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
            if (bfd.singularData.getSingularizer().requiresCleaning()) {
                addCleaning = true;
                break;
            }
        }
        if (bfd.obtainVia != null) {
            if (bfd.obtainVia.field().isEmpty() == bfd.obtainVia.method().isEmpty()) {
                bfd.obtainViaNode.addError(
                        "The syntax is either @ObtainVia(field = \"fieldName\") or @ObtainVia(method = \"methodName\").");
                return;
            }
            if (bfd.obtainVia.method().isEmpty() && bfd.obtainVia.isStatic()) {
                bfd.obtainViaNode
                        .addError("@ObtainVia(isStatic = true) is not valid unless 'method' has been set.");
                return;
            }
        }
    }

    // Generate the fields in the abstract builder class that hold the values for the instance.
    generateBuilderFields(builderType, builderFields, ast);
    if (addCleaning) {
        FieldDeclaration cleanDecl = new FieldDeclaration(CLEAN_FIELD_NAME, 0, -1);
        cleanDecl.declarationSourceEnd = -1;
        cleanDecl.modifiers = ClassFileConstants.AccPrivate;
        cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
        injectFieldAndMarkGenerated(builderType, cleanDecl);
    }

    if (toBuilder) {
        // Generate $fillValuesFrom() method in the abstract builder.
        injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null,
                builderGenericName, classGenericName, builderClassName, typeParams));
        // Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
        injectMethod(builderType,
                generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields, ast));
    }

    // Generate abstract self() and build() methods in the abstract builder.
    injectMethod(builderType,
            generateAbstractSelfMethod(tdParent, superclassBuilderClass != null, builderGenericName));
    injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName,
            superclassBuilderClass != null, classGenericName, ast));

    // Create the setter methods in the abstract builder.
    for (BuilderFieldData bfd : builderFields) {
        generateSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName);
    }

    // Create the toString() method for the abstract builder.
    if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
        List<Included<EclipseNode, ToString.Include>> fieldNodes = new ArrayList<Included<EclipseNode, ToString.Include>>();
        for (BuilderFieldData bfd : builderFields) {
            for (EclipseNode f : bfd.createdFields) {
                fieldNodes.add(new Included<EclipseNode, ToString.Include>(f, null, true));
            }
        }
        // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder.
        MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true,
                superclassBuilderClass != null, ast, FieldAccess.ALWAYS_FIELD);
        if (md != null) {
            injectMethod(builderType, md);
        }
    }

    if (addCleaning)
        injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));

    boolean isAbstract = (td.modifiers & ClassFileConstants.AccAbstract) != 0;
    if (isAbstract) {
        // Only non-abstract classes get the Builder implementation.
        return;
    }

    // Create the builder implementation class, or reuse an existing one.
    EclipseNode builderImplType = findInnerClass(tdParent, builderImplClassName);
    if (builderImplType == null) {
        builderImplType = generateBuilderImplClass(tdParent, builderImplClassName, builderClassName, typeParams,
                ast);
    } else {
        TypeDeclaration builderImplTypeDeclaration = (TypeDeclaration) builderImplType.get();
        if ((builderImplTypeDeclaration.modifiers & ClassFileConstants.AccAbstract) != 0
                || (builderImplTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) {
            annotationNode.addError("Existing BuilderImpl must be a non-abstract static inner class.");
            return;
        }
        sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderImplType, annotationNode);
    }

    if (toBuilder) {
        // Add the toBuilder() method to the annotated class.
        switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, tdParent, 0)) {
        case EXISTS_BY_USER:
            annotationNode.addWarning("Not generating toBuilder() as it already exists.");
            break;
        case NOT_EXISTS:
            injectMethod(tdParent,
                    generateToBuilderMethod(builderClassName, builderImplClassName, tdParent, typeParams, ast));
        default:
            // Should not happen.
        }
    }

    // Create the self() and build() methods in the BuilderImpl.
    injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams, p));
    if (methodExists(buildMethodName, builderImplType, -1) == MemberExistsResult.NOT_EXISTS) {
        injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, ast));
    }

    // Add the builder() method to the annotated class.
    if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS)
        generateBuilderMethod = false;
    if (generateBuilderMethod) {
        MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName,
                tdParent, typeParams, ast);
        if (md != null)
            injectMethod(tdParent, md);
    }

    if (nonFinalNonDefaultedFields != null && generateBuilderMethod) {
        for (EclipseNode fieldNode : nonFinalNonDefaultedFields) {
            fieldNode.addWarning(
                    "@SuperBuilder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.");
        }
    }
}