Search in sources :

Example 21 with MethodDeclaration

use of org.eclipse.jdt.internal.compiler.ast.MethodDeclaration in project lombok by rzwitserloot.

the class HandleEqualsAndHashCode method generateMethods.

public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<String> excludes, List<String> includes, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<Annotation> onParam) {
    assert excludes == null || includes == null;
    TypeDeclaration typeDecl = null;
    if (typeNode.get() instanceof TypeDeclaration)
        typeDecl = (TypeDeclaration) typeNode.get();
    int modifiers = typeDecl == null ? 0 : typeDecl.modifiers;
    boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0;
    if (typeDecl == null || notAClass) {
        errorNode.addError("@EqualsAndHashCode is only supported on a class.");
        return;
    }
    boolean implicitCallSuper = callSuper == null;
    if (callSuper == null) {
        try {
            callSuper = ((Boolean) EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
        } catch (Exception ignore) {
            throw new InternalError("Lombok bug - this cannot happen - can't find callSuper field in EqualsAndHashCode annotation.");
        }
    }
    boolean isDirectDescendantOfObject = true;
    if (typeDecl.superclass != null) {
        String p = typeDecl.superclass.toString();
        isDirectDescendantOfObject = p.equals("Object") || p.equals("java.lang.Object");
    }
    if (isDirectDescendantOfObject && callSuper) {
        errorNode.addError("Generating equals/hashCode with a supercall to java.lang.Object is pointless.");
        return;
    }
    if (implicitCallSuper && !isDirectDescendantOfObject) {
        CallSuperType cst = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_CALL_SUPER);
        if (cst == null)
            cst = CallSuperType.WARN;
        switch(cst) {
            default:
            case WARN:
                errorNode.addWarning("Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type.");
                callSuper = false;
                break;
            case SKIP:
                callSuper = false;
                break;
            case CALL:
                callSuper = true;
                break;
        }
    }
    List<EclipseNode> nodesForEquality = new ArrayList<EclipseNode>();
    if (includes != null) {
        for (EclipseNode child : typeNode.down()) {
            if (child.getKind() != Kind.FIELD)
                continue;
            FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
            if (includes.contains(new String(fieldDecl.name)))
                nodesForEquality.add(child);
        }
    } else {
        for (EclipseNode child : typeNode.down()) {
            if (child.getKind() != Kind.FIELD)
                continue;
            FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
            if (!filterField(fieldDecl))
                continue;
            //Skip transient fields.
            if ((fieldDecl.modifiers & ClassFileConstants.AccTransient) != 0)
                continue;
            //Skip excluded fields.
            if (excludes != null && excludes.contains(new String(fieldDecl.name)))
                continue;
            nodesForEquality.add(child);
        }
    }
    boolean isFinal = (typeDecl.modifiers & ClassFileConstants.AccFinal) != 0;
    boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
    MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
    MemberExistsResult hashCodeExists = methodExists("hashCode", typeNode, 0);
    MemberExistsResult canEqualExists = methodExists("canEqual", typeNode, 1);
    switch(Collections.max(Arrays.asList(equalsExists, hashCodeExists))) {
        case EXISTS_BY_LOMBOK:
            return;
        case EXISTS_BY_USER:
            if (whineIfExists) {
                String msg = "Not generating equals and hashCode: A method with one of those names already exists. (Either both or none of these methods will be generated).";
                errorNode.addWarning(msg);
            } else if (equalsExists == MemberExistsResult.NOT_EXISTS || hashCodeExists == MemberExistsResult.NOT_EXISTS) {
                // This means equals OR hashCode exists and not both.
                // Even though we should suppress the message about not generating these, this is such a weird and surprising situation we should ALWAYS generate a warning.
                // The user code couldn't possibly (barring really weird subclassing shenanigans) be in a shippable state anyway; the implementations of these 2 methods are
                // all inter-related and should be written by the same entity.
                String msg = String.format("Not generating %s: One of equals or hashCode exists. " + "You should either write both of these or none of these (in the latter case, lombok generates them).", equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
                errorNode.addWarning(msg);
            }
            return;
        case NOT_EXISTS:
        default:
    }
    MethodDeclaration equalsMethod = createEquals(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess, needsCanEqual, onParam);
    equalsMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration) typeNode.get()).scope);
    injectMethod(typeNode, equalsMethod);
    if (needsCanEqual && canEqualExists == MemberExistsResult.NOT_EXISTS) {
        MethodDeclaration canEqualMethod = createCanEqual(typeNode, errorNode.get(), onParam);
        canEqualMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration) typeNode.get()).scope);
        injectMethod(typeNode, canEqualMethod);
    }
    MethodDeclaration hashCodeMethod = createHashCode(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess);
    hashCodeMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration) typeNode.get()).scope);
    injectMethod(typeNode, hashCodeMethod);
}
Also used : MemberExistsResult(lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult) MethodDeclaration(org.eclipse.jdt.internal.compiler.ast.MethodDeclaration) EqualsAndHashCode(lombok.EqualsAndHashCode) ArrayList(java.util.ArrayList) FieldDeclaration(org.eclipse.jdt.internal.compiler.ast.FieldDeclaration) EclipseNode(lombok.eclipse.EclipseNode) CallSuperType(lombok.core.configuration.CallSuperType) TypeDeclaration(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration)

Example 22 with MethodDeclaration

use of org.eclipse.jdt.internal.compiler.ast.MethodDeclaration in project lombok by rzwitserloot.

the class HandleEqualsAndHashCode method createHashCode.

public MethodDeclaration createHashCode(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
    int pS = source.sourceStart, pE = source.sourceEnd;
    long p = (long) pS << 32 | pE;
    MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
    setGeneratedBy(method, source);
    method.modifiers = toEclipseModifier(AccessLevel.PUBLIC);
    method.returnType = TypeReference.baseTypeReference(TypeIds.T_int, 0);
    setGeneratedBy(method.returnType, source);
    method.annotations = new Annotation[] { makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source) };
    method.selector = "hashCode".toCharArray();
    method.thrownExceptions = null;
    method.typeParameters = null;
    method.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
    method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart;
    method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd;
    method.arguments = null;
    List<Statement> statements = new ArrayList<Statement>();
    final boolean isEmpty = fields.isEmpty();
    /* final int PRIME = X; */
    {
        /* Without fields, PRIME isn't used, and that would trigger a 'local variable not used' warning. */
        if (!isEmpty || callSuper) {
            LocalDeclaration primeDecl = new LocalDeclaration(PRIME, pS, pE);
            setGeneratedBy(primeDecl, source);
            primeDecl.modifiers |= Modifier.FINAL;
            primeDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0);
            primeDecl.type.sourceStart = pS;
            primeDecl.type.sourceEnd = pE;
            setGeneratedBy(primeDecl.type, source);
            primeDecl.initialization = makeIntLiteral(String.valueOf(HandlerUtil.primeForHashcode()).toCharArray(), source);
            statements.add(primeDecl);
        }
    }
    /* int result = 1; */
    {
        LocalDeclaration resultDecl = new LocalDeclaration(RESULT, pS, pE);
        setGeneratedBy(resultDecl, source);
        resultDecl.initialization = makeIntLiteral("1".toCharArray(), source);
        resultDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0);
        resultDecl.type.sourceStart = pS;
        resultDecl.type.sourceEnd = pE;
        setGeneratedBy(resultDecl.type, source);
        statements.add(resultDecl);
    }
    if (callSuper) {
        MessageSend callToSuper = new MessageSend();
        setGeneratedBy(callToSuper, source);
        callToSuper.sourceStart = pS;
        callToSuper.sourceEnd = pE;
        callToSuper.receiver = new SuperReference(pS, pE);
        setGeneratedBy(callToSuper.receiver, source);
        callToSuper.selector = "hashCode".toCharArray();
        statements.add(createResultCalculation(source, callToSuper));
    }
    for (EclipseNode field : fields) {
        TypeReference fType = getFieldType(field, fieldAccess);
        char[] dollarFieldName = ("$" + field.getName()).toCharArray();
        char[] token = fType.getLastToken();
        Expression fieldAccessor = createFieldAccessor(field, fieldAccess, source);
        if (fType.dimensions() == 0 && token != null) {
            if (Arrays.equals(TypeConstants.BOOLEAN, token)) {
                /* booleanField ? X : Y */
                IntLiteral intTrue = makeIntLiteral(String.valueOf(HandlerUtil.primeForTrue()).toCharArray(), source);
                IntLiteral intFalse = makeIntLiteral(String.valueOf(HandlerUtil.primeForFalse()).toCharArray(), source);
                ConditionalExpression intForBool = new ConditionalExpression(fieldAccessor, intTrue, intFalse);
                setGeneratedBy(intForBool, source);
                statements.add(createResultCalculation(source, intForBool));
            } else if (Arrays.equals(TypeConstants.LONG, token)) {
                statements.add(createLocalDeclaration(source, dollarFieldName, TypeReference.baseTypeReference(TypeIds.T_long, 0), fieldAccessor));
                SingleNameReference copy1 = new SingleNameReference(dollarFieldName, p);
                setGeneratedBy(copy1, source);
                SingleNameReference copy2 = new SingleNameReference(dollarFieldName, p);
                setGeneratedBy(copy2, source);
                statements.add(createResultCalculation(source, longToIntForHashCode(copy1, copy2, source)));
            } else if (Arrays.equals(TypeConstants.FLOAT, token)) {
                /* Float.floatToIntBits(fieldName) */
                MessageSend floatToIntBits = new MessageSend();
                floatToIntBits.sourceStart = pS;
                floatToIntBits.sourceEnd = pE;
                setGeneratedBy(floatToIntBits, source);
                floatToIntBits.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_FLOAT);
                floatToIntBits.selector = "floatToIntBits".toCharArray();
                floatToIntBits.arguments = new Expression[] { fieldAccessor };
                statements.add(createResultCalculation(source, floatToIntBits));
            } else if (Arrays.equals(TypeConstants.DOUBLE, token)) {
                /* longToIntForHashCode(Double.doubleToLongBits(fieldName)) */
                MessageSend doubleToLongBits = new MessageSend();
                doubleToLongBits.sourceStart = pS;
                doubleToLongBits.sourceEnd = pE;
                setGeneratedBy(doubleToLongBits, source);
                doubleToLongBits.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_DOUBLE);
                doubleToLongBits.selector = "doubleToLongBits".toCharArray();
                doubleToLongBits.arguments = new Expression[] { fieldAccessor };
                statements.add(createLocalDeclaration(source, dollarFieldName, TypeReference.baseTypeReference(TypeIds.T_long, 0), doubleToLongBits));
                SingleNameReference copy1 = new SingleNameReference(dollarFieldName, p);
                setGeneratedBy(copy1, source);
                SingleNameReference copy2 = new SingleNameReference(dollarFieldName, p);
                setGeneratedBy(copy2, source);
                statements.add(createResultCalculation(source, longToIntForHashCode(copy1, copy2, source)));
            } else if (BUILT_IN_TYPES.contains(new String(token))) {
                statements.add(createResultCalculation(source, fieldAccessor));
            } else /* objects */
            {
                /* final java.lang.Object $fieldName = this.fieldName; */
                /* $fieldName == null ? NULL_PRIME : $fieldName.hashCode() */
                statements.add(createLocalDeclaration(source, dollarFieldName, generateQualifiedTypeRef(source, TypeConstants.JAVA_LANG_OBJECT), fieldAccessor));
                SingleNameReference copy1 = new SingleNameReference(dollarFieldName, p);
                setGeneratedBy(copy1, source);
                SingleNameReference copy2 = new SingleNameReference(dollarFieldName, p);
                setGeneratedBy(copy2, source);
                MessageSend hashCodeCall = new MessageSend();
                hashCodeCall.sourceStart = pS;
                hashCodeCall.sourceEnd = pE;
                setGeneratedBy(hashCodeCall, source);
                hashCodeCall.receiver = copy1;
                hashCodeCall.selector = "hashCode".toCharArray();
                NullLiteral nullLiteral = new NullLiteral(pS, pE);
                setGeneratedBy(nullLiteral, source);
                EqualExpression objIsNull = new EqualExpression(copy2, nullLiteral, OperatorIds.EQUAL_EQUAL);
                setGeneratedBy(objIsNull, source);
                IntLiteral intMagic = makeIntLiteral(String.valueOf(HandlerUtil.primeForNull()).toCharArray(), source);
                ConditionalExpression nullOrHashCode = new ConditionalExpression(objIsNull, intMagic, hashCodeCall);
                nullOrHashCode.sourceStart = pS;
                nullOrHashCode.sourceEnd = pE;
                setGeneratedBy(nullOrHashCode, source);
                statements.add(createResultCalculation(source, nullOrHashCode));
            }
        } else if (fType.dimensions() > 0 && token != null) {
            /* Arrays.deepHashCode(array)  //just hashCode for simple arrays */
            MessageSend arraysHashCodeCall = new MessageSend();
            arraysHashCodeCall.sourceStart = pS;
            arraysHashCodeCall.sourceEnd = pE;
            setGeneratedBy(arraysHashCodeCall, source);
            arraysHashCodeCall.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Arrays".toCharArray());
            if (fType.dimensions() > 1 || !BUILT_IN_TYPES.contains(new String(token))) {
                arraysHashCodeCall.selector = "deepHashCode".toCharArray();
            } else {
                arraysHashCodeCall.selector = "hashCode".toCharArray();
            }
            arraysHashCodeCall.arguments = new Expression[] { fieldAccessor };
            statements.add(createResultCalculation(source, arraysHashCodeCall));
        }
    }
    /* return result; */
    {
        SingleNameReference resultRef = new SingleNameReference(RESULT, p);
        setGeneratedBy(resultRef, source);
        ReturnStatement returnStatement = new ReturnStatement(resultRef, pS, pE);
        setGeneratedBy(returnStatement, source);
        statements.add(returnStatement);
    }
    method.statements = statements.toArray(new Statement[statements.size()]);
    return method;
}
Also used : LocalDeclaration(org.eclipse.jdt.internal.compiler.ast.LocalDeclaration) MethodDeclaration(org.eclipse.jdt.internal.compiler.ast.MethodDeclaration) Statement(org.eclipse.jdt.internal.compiler.ast.Statement) ReturnStatement(org.eclipse.jdt.internal.compiler.ast.ReturnStatement) IfStatement(org.eclipse.jdt.internal.compiler.ast.IfStatement) ConditionalExpression(org.eclipse.jdt.internal.compiler.ast.ConditionalExpression) ArrayList(java.util.ArrayList) EqualExpression(org.eclipse.jdt.internal.compiler.ast.EqualExpression) SingleNameReference(org.eclipse.jdt.internal.compiler.ast.SingleNameReference) SuperReference(org.eclipse.jdt.internal.compiler.ast.SuperReference) MessageSend(org.eclipse.jdt.internal.compiler.ast.MessageSend) Expression(org.eclipse.jdt.internal.compiler.ast.Expression) BinaryExpression(org.eclipse.jdt.internal.compiler.ast.BinaryExpression) ConditionalExpression(org.eclipse.jdt.internal.compiler.ast.ConditionalExpression) InstanceOfExpression(org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression) UnaryExpression(org.eclipse.jdt.internal.compiler.ast.UnaryExpression) EqualExpression(org.eclipse.jdt.internal.compiler.ast.EqualExpression) CastExpression(org.eclipse.jdt.internal.compiler.ast.CastExpression) ReturnStatement(org.eclipse.jdt.internal.compiler.ast.ReturnStatement) EclipseNode(lombok.eclipse.EclipseNode) IntLiteral(org.eclipse.jdt.internal.compiler.ast.IntLiteral) TypeReference(org.eclipse.jdt.internal.compiler.ast.TypeReference) ParameterizedSingleTypeReference(org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference) QualifiedTypeReference(org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference) ParameterizedQualifiedTypeReference(org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference) SingleTypeReference(org.eclipse.jdt.internal.compiler.ast.SingleTypeReference) NullLiteral(org.eclipse.jdt.internal.compiler.ast.NullLiteral)

Example 23 with MethodDeclaration

use of org.eclipse.jdt.internal.compiler.ast.MethodDeclaration in project lombok by rzwitserloot.

the class HandleEqualsAndHashCode method createCanEqual.

public MethodDeclaration createCanEqual(EclipseNode type, ASTNode source, List<Annotation> onParam) {
    /* protected boolean canEqual(final java.lang.Object other) {
		 *     return other instanceof Outer.Inner.MyType;
		 * }
		 */
    int pS = source.sourceStart;
    int pE = source.sourceEnd;
    long p = (long) pS << 32 | pE;
    char[] otherName = "other".toCharArray();
    MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
    setGeneratedBy(method, source);
    method.modifiers = toEclipseModifier(AccessLevel.PROTECTED);
    method.returnType = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
    method.returnType.sourceStart = pS;
    method.returnType.sourceEnd = pE;
    setGeneratedBy(method.returnType, source);
    method.selector = "canEqual".toCharArray();
    method.thrownExceptions = null;
    method.typeParameters = null;
    method.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
    method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart;
    method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd;
    TypeReference objectRef = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, new long[] { p, p, p });
    setGeneratedBy(objectRef, source);
    method.arguments = new Argument[] { new Argument(otherName, 0, objectRef, Modifier.FINAL) };
    method.arguments[0].sourceStart = pS;
    method.arguments[0].sourceEnd = pE;
    if (!onParam.isEmpty())
        method.arguments[0].annotations = onParam.toArray(new Annotation[0]);
    setGeneratedBy(method.arguments[0], source);
    SingleNameReference otherRef = new SingleNameReference(otherName, p);
    setGeneratedBy(otherRef, source);
    TypeReference typeReference = createTypeReference(type, p, source, false);
    setGeneratedBy(typeReference, source);
    InstanceOfExpression instanceOf = new InstanceOfExpression(otherRef, typeReference);
    instanceOf.sourceStart = pS;
    instanceOf.sourceEnd = pE;
    setGeneratedBy(instanceOf, source);
    ReturnStatement returnStatement = new ReturnStatement(instanceOf, pS, pE);
    setGeneratedBy(returnStatement, source);
    method.statements = new Statement[] { returnStatement };
    return method;
}
Also used : Argument(org.eclipse.jdt.internal.compiler.ast.Argument) MethodDeclaration(org.eclipse.jdt.internal.compiler.ast.MethodDeclaration) QualifiedTypeReference(org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference) ParameterizedQualifiedTypeReference(org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference) ReturnStatement(org.eclipse.jdt.internal.compiler.ast.ReturnStatement) TypeReference(org.eclipse.jdt.internal.compiler.ast.TypeReference) ParameterizedSingleTypeReference(org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference) QualifiedTypeReference(org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference) ParameterizedQualifiedTypeReference(org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference) SingleTypeReference(org.eclipse.jdt.internal.compiler.ast.SingleTypeReference) InstanceOfExpression(org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression) SingleNameReference(org.eclipse.jdt.internal.compiler.ast.SingleNameReference)

Example 24 with MethodDeclaration

use of org.eclipse.jdt.internal.compiler.ast.MethodDeclaration in project lombok by rzwitserloot.

the class HandleBuilder method handle.

@Override
public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) {
    long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
    Builder builderInstance = annotation.getInstance();
    // These exist just to support the 'old' lombok.experimental.Builder, which had these properties. lombok.Builder no longer has them.
    boolean fluent = toBoolean(annotation.getActualExpression("fluent"), true);
    boolean chain = toBoolean(annotation.getActualExpression("chain"), true);
    String builderMethodName = builderInstance.builderMethodName();
    String buildMethodName = builderInstance.buildMethodName();
    String builderClassName = builderInstance.builderClassName();
    String toBuilderMethodName = "toBuilder";
    boolean toBuilder = builderInstance.toBuilder();
    List<char[]> typeArgsForToBuilder = null;
    if (builderMethodName == null)
        builderMethodName = "builder";
    if (buildMethodName == null)
        builderMethodName = "build";
    if (builderClassName == null)
        builderClassName = "";
    if (!checkName("builderMethodName", builderMethodName, annotationNode))
        return;
    if (!checkName("buildMethodName", buildMethodName, annotationNode))
        return;
    if (!builderClassName.isEmpty()) {
        if (!checkName("builderClassName", builderClassName, annotationNode))
            return;
    }
    EclipseNode parent = annotationNode.up();
    List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
    TypeReference returnType;
    TypeParameter[] typeParams;
    TypeReference[] thrownExceptions;
    char[] nameOfStaticBuilderMethod;
    EclipseNode tdParent;
    EclipseNode fillParametersFrom = parent.get() instanceof AbstractMethodDeclaration ? parent : null;
    boolean addCleaning = false;
    boolean isStatic = true;
    if (parent.get() instanceof TypeDeclaration) {
        tdParent = parent;
        TypeDeclaration td = (TypeDeclaration) tdParent.get();
        List<EclipseNode> allFields = new ArrayList<EclipseNode>();
        @SuppressWarnings("deprecation") boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
        for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
            FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
            // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
            if (fd.initialization != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode))
                continue;
            BuilderFieldData bfd = new BuilderFieldData();
            bfd.rawName = fieldNode.getName().toCharArray();
            bfd.name = removePrefixFromField(fieldNode);
            bfd.type = fd.type;
            bfd.singularData = getSingularData(fieldNode, ast);
            addObtainVia(bfd, fieldNode);
            builderFields.add(bfd);
            allFields.add(fieldNode);
        }
        new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, Collections.<Annotation>emptyList(), annotationNode);
        returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
        typeParams = td.typeParameters;
        thrownExceptions = null;
        nameOfStaticBuilderMethod = null;
        if (builderClassName.isEmpty())
            builderClassName = new String(td.name) + "Builder";
    } else if (parent.get() instanceof ConstructorDeclaration) {
        ConstructorDeclaration cd = (ConstructorDeclaration) parent.get();
        if (cd.typeParameters != null && cd.typeParameters.length > 0) {
            annotationNode.addError("@Builder is not supported on constructors with constructor type parameters.");
            return;
        }
        tdParent = parent.up();
        TypeDeclaration td = (TypeDeclaration) tdParent.get();
        returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
        typeParams = td.typeParameters;
        thrownExceptions = cd.thrownExceptions;
        nameOfStaticBuilderMethod = null;
        if (builderClassName.isEmpty())
            builderClassName = new String(cd.selector) + "Builder";
    } else if (parent.get() instanceof MethodDeclaration) {
        MethodDeclaration md = (MethodDeclaration) parent.get();
        tdParent = parent.up();
        isStatic = md.isStatic();
        if (toBuilder) {
            final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type.";
            char[] token;
            char[][] pkg = null;
            if (md.returnType.dimensions() > 0) {
                annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
                return;
            }
            if (md.returnType instanceof SingleTypeReference) {
                token = ((SingleTypeReference) md.returnType).token;
            } else if (md.returnType instanceof QualifiedTypeReference) {
                pkg = ((QualifiedTypeReference) md.returnType).tokens;
                token = pkg[pkg.length];
                char[][] pkg_ = new char[pkg.length - 1][];
                System.arraycopy(pkg, 0, pkg_, 0, pkg_.length);
                pkg = pkg_;
            } else {
                annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
                return;
            }
            if (pkg != null && !equals(parent.getPackageDeclaration(), pkg)) {
                annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
                return;
            }
            if (tdParent == null || !equals(tdParent.getName(), token)) {
                annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
                return;
            }
            TypeParameter[] tpOnType = ((TypeDeclaration) tdParent.get()).typeParameters;
            TypeParameter[] tpOnMethod = md.typeParameters;
            TypeReference[][] tpOnRet_ = null;
            if (md.returnType instanceof ParameterizedSingleTypeReference) {
                tpOnRet_ = new TypeReference[1][];
                tpOnRet_[0] = ((ParameterizedSingleTypeReference) md.returnType).typeArguments;
            } else if (md.returnType instanceof ParameterizedQualifiedTypeReference) {
                tpOnRet_ = ((ParameterizedQualifiedTypeReference) md.returnType).typeArguments;
            }
            if (tpOnRet_ != null)
                for (int i = 0; i < tpOnRet_.length - 1; i++) {
                    if (tpOnRet_[i] != null && tpOnRet_[i].length > 0) {
                        annotationNode.addError("@Builder(toBuilder=true) is not supported if returning a type with generics applied to an intermediate.");
                        return;
                    }
                }
            TypeReference[] tpOnRet = tpOnRet_ == null ? null : tpOnRet_[tpOnRet_.length - 1];
            typeArgsForToBuilder = new ArrayList<char[]>();
            if (tpOnMethod != null)
                for (TypeParameter onMethod : tpOnMethod) {
                    int pos = -1;
                    if (tpOnRet != null)
                        for (int i = 0; i < tpOnRet.length; i++) {
                            if (tpOnRet[i].getClass() != SingleTypeReference.class)
                                continue;
                            if (!Arrays.equals(((SingleTypeReference) tpOnRet[i]).token, onMethod.name))
                                continue;
                            pos = i;
                        }
                    if (pos == -1 || tpOnType == null || tpOnType.length <= pos) {
                        annotationNode.addError("@Builder(toBuilder=true) requires that each type parameter on the static method is part of the typeargs of the return value. Type parameter " + new String(onMethod.name) + " is not part of the return type.");
                        return;
                    }
                    typeArgsForToBuilder.add(tpOnType[pos].name);
                }
        }
        returnType = copyType(md.returnType, ast);
        typeParams = md.typeParameters;
        thrownExceptions = md.thrownExceptions;
        nameOfStaticBuilderMethod = md.selector;
        if (builderClassName.isEmpty()) {
            char[] token;
            if (md.returnType instanceof QualifiedTypeReference) {
                char[][] tokens = ((QualifiedTypeReference) md.returnType).tokens;
                token = tokens[tokens.length - 1];
            } else if (md.returnType instanceof SingleTypeReference) {
                token = ((SingleTypeReference) md.returnType).token;
                if (!(md.returnType instanceof ParameterizedSingleTypeReference) && typeParams != null) {
                    for (TypeParameter tp : typeParams) {
                        if (Arrays.equals(tp.name, token)) {
                            annotationNode.addError("@Builder requires specifying 'builderClassName' if used on methods with a type parameter as return type.");
                            return;
                        }
                    }
                }
            } else {
                annotationNode.addError("Unexpected kind of return type on annotated method. Specify 'builderClassName' to solve this problem.");
                return;
            }
            if (Character.isLowerCase(token[0])) {
                char[] newToken = new char[token.length];
                System.arraycopy(token, 1, newToken, 1, token.length - 1);
                newToken[0] = Character.toTitleCase(token[0]);
                token = newToken;
            }
            builderClassName = new String(token) + "Builder";
        }
    } else {
        annotationNode.addError("@Builder is only supported on types, constructors, and methods.");
        return;
    }
    if (fillParametersFrom != null) {
        for (EclipseNode param : fillParametersFrom.down()) {
            if (param.getKind() != Kind.ARGUMENT)
                continue;
            BuilderFieldData bfd = new BuilderFieldData();
            Argument arg = (Argument) param.get();
            bfd.rawName = arg.name;
            bfd.name = arg.name;
            bfd.type = arg.type;
            bfd.singularData = getSingularData(param, ast);
            addObtainVia(bfd, param);
            builderFields.add(bfd);
        }
    }
    EclipseNode builderType = findInnerClass(tdParent, builderClassName);
    if (builderType == null) {
        builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast);
    } else {
        TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get();
        if (isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) {
            annotationNode.addError("Existing Builder must be a static inner class.");
            return;
        } else if (!isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) != 0) {
            annotationNode.addError("Existing Builder must be a non-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;
                }
            }
        }
    }
    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;
            }
        }
    }
    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 (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
        ConstructorDeclaration cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), false, annotationNode, Collections.<Annotation>emptyList());
        if (cd != null)
            injectMethod(builderType, cd);
    }
    for (BuilderFieldData bfd : builderFields) {
        makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain);
    }
    if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
        MethodDeclaration md = generateBuildMethod(isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast);
        if (md != null)
            injectMethod(builderType, md);
    }
    if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
        List<EclipseNode> fieldNodes = new ArrayList<EclipseNode>();
        for (BuilderFieldData bfd : builderFields) {
            fieldNodes.addAll(bfd.createdFields);
        }
        MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD);
        if (md != null)
            injectMethod(builderType, md);
    }
    if (addCleaning) {
        MethodDeclaration cleanMethod = generateCleanMethod(builderFields, builderType, ast);
        if (cleanMethod != null)
            injectMethod(builderType, cleanMethod);
    }
    if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
        MethodDeclaration md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast);
        if (md != null)
            injectMethod(tdParent, md);
    }
    if (toBuilder)
        switch(methodExists(toBuilderMethodName, tdParent, 0)) {
            case EXISTS_BY_USER:
                annotationNode.addWarning("Not generating toBuilder() as it already exists.");
                break;
            case NOT_EXISTS:
                TypeParameter[] tps = typeParams;
                if (typeArgsForToBuilder != null) {
                    tps = new TypeParameter[typeArgsForToBuilder.size()];
                    for (int i = 0; i < tps.length; i++) {
                        tps[i] = new TypeParameter();
                        tps[i].name = typeArgsForToBuilder.get(i);
                    }
                }
                MethodDeclaration md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast);
                if (md != null)
                    injectMethod(tdParent, md);
        }
}
Also used : TypeParameter(org.eclipse.jdt.internal.compiler.ast.TypeParameter) Argument(org.eclipse.jdt.internal.compiler.ast.Argument) Builder(lombok.Builder) ArrayList(java.util.ArrayList) FieldDeclaration(org.eclipse.jdt.internal.compiler.ast.FieldDeclaration) ConstructorDeclaration(org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration) QualifiedTypeReference(org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference) ParameterizedQualifiedTypeReference(org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference) ParameterizedSingleTypeReference(org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference) EclipseSingularizer(lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer) TypeReference(org.eclipse.jdt.internal.compiler.ast.TypeReference) ParameterizedSingleTypeReference(org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference) QualifiedTypeReference(org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference) ParameterizedQualifiedTypeReference(org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference) SingleTypeReference(org.eclipse.jdt.internal.compiler.ast.SingleTypeReference) MethodDeclaration(org.eclipse.jdt.internal.compiler.ast.MethodDeclaration) AbstractMethodDeclaration(org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration) ParameterizedQualifiedTypeReference(org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference) ParameterizedSingleTypeReference(org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference) SingleTypeReference(org.eclipse.jdt.internal.compiler.ast.SingleTypeReference) SingularData(lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData) EclipseNode(lombok.eclipse.EclipseNode) AbstractMethodDeclaration(org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration) TypeDeclaration(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration)

Example 25 with MethodDeclaration

use of org.eclipse.jdt.internal.compiler.ast.MethodDeclaration in project lombok by rzwitserloot.

the class HandleBuilder method generateBuilderMethod.

public MethodDeclaration generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
    int pS = source.sourceStart, pE = source.sourceEnd;
    long p = (long) pS << 32 | pE;
    MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
    out.selector = builderMethodName.toCharArray();
    out.modifiers = ClassFileConstants.AccPublic;
    if (isStatic)
        out.modifiers |= ClassFileConstants.AccStatic;
    out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
    out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
    out.typeParameters = copyTypeParams(typeParams, source);
    AllocationExpression invoke = new AllocationExpression();
    invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
    out.statements = new Statement[] { new ReturnStatement(invoke, pS, pE) };
    out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
    return out;
}
Also used : AllocationExpression(org.eclipse.jdt.internal.compiler.ast.AllocationExpression) MethodDeclaration(org.eclipse.jdt.internal.compiler.ast.MethodDeclaration) AbstractMethodDeclaration(org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration) ReturnStatement(org.eclipse.jdt.internal.compiler.ast.ReturnStatement)

Aggregations

MethodDeclaration (org.eclipse.jdt.internal.compiler.ast.MethodDeclaration)40 ReturnStatement (org.eclipse.jdt.internal.compiler.ast.ReturnStatement)22 Statement (org.eclipse.jdt.internal.compiler.ast.Statement)19 TypeReference (org.eclipse.jdt.internal.compiler.ast.TypeReference)19 ArrayList (java.util.ArrayList)18 ThisReference (org.eclipse.jdt.internal.compiler.ast.ThisReference)17 EclipseNode (lombok.eclipse.EclipseNode)16 SingleNameReference (org.eclipse.jdt.internal.compiler.ast.SingleNameReference)16 AbstractMethodDeclaration (org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration)15 Argument (org.eclipse.jdt.internal.compiler.ast.Argument)15 MessageSend (org.eclipse.jdt.internal.compiler.ast.MessageSend)15 QualifiedTypeReference (org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference)15 FieldDeclaration (org.eclipse.jdt.internal.compiler.ast.FieldDeclaration)14 IfStatement (org.eclipse.jdt.internal.compiler.ast.IfStatement)14 FieldReference (org.eclipse.jdt.internal.compiler.ast.FieldReference)13 TypeDeclaration (org.eclipse.jdt.internal.compiler.ast.TypeDeclaration)12 Expression (org.eclipse.jdt.internal.compiler.ast.Expression)11 SingleTypeReference (org.eclipse.jdt.internal.compiler.ast.SingleTypeReference)10 ParameterizedQualifiedTypeReference (org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference)8 ASTNode (org.eclipse.jdt.internal.compiler.ast.ASTNode)6