Search in sources :

Example 1 with EqualsAndHashCode

use of lombok.EqualsAndHashCode in project lombok by rzwitserloot.

the class HandleEqualsAndHashCode method generateMethods.

public void generateMethods(JavacNode typeNode, JavacNode source, List<String> excludes, List<String> includes, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<JCAnnotation> onParam) {
    boolean notAClass = true;
    if (typeNode.get() instanceof JCClassDecl) {
        long flags = ((JCClassDecl) typeNode.get()).mods.flags;
        notAClass = (flags & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0;
    }
    if (notAClass) {
        source.addError("@EqualsAndHashCode is only supported on a class.");
        return;
    }
    boolean isDirectDescendantOfObject = true;
    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.");
        }
    }
    JCTree extending = Javac.getExtendsClause((JCClassDecl) typeNode.get());
    if (extending != null) {
        String p = extending.toString();
        isDirectDescendantOfObject = p.equals("Object") || p.equals("java.lang.Object");
    }
    if (isDirectDescendantOfObject && callSuper) {
        source.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:
                source.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;
        }
    }
    ListBuffer<JavacNode> nodesForEquality = new ListBuffer<JavacNode>();
    if (includes != null) {
        for (JavacNode child : typeNode.down()) {
            if (child.getKind() != Kind.FIELD)
                continue;
            JCVariableDecl fieldDecl = (JCVariableDecl) child.get();
            if (includes.contains(fieldDecl.name.toString()))
                nodesForEquality.append(child);
        }
    } else {
        for (JavacNode child : typeNode.down()) {
            if (child.getKind() != Kind.FIELD)
                continue;
            JCVariableDecl fieldDecl = (JCVariableDecl) child.get();
            // Skip static fields.
            if ((fieldDecl.mods.flags & Flags.STATIC) != 0)
                continue;
            // Skip transient fields.
            if ((fieldDecl.mods.flags & Flags.TRANSIENT) != 0)
                continue;
            // Skip excluded fields.
            if (excludes != null && excludes.contains(fieldDecl.name.toString()))
                continue;
            // Skip fields that start with $
            if (fieldDecl.name.toString().startsWith("$"))
                continue;
            nodesForEquality.append(child);
        }
    }
    boolean isFinal = (((JCClassDecl) typeNode.get()).mods.flags & Flags.FINAL) != 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).";
                source.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");
                source.addWarning(msg);
            }
            return;
        case NOT_EXISTS:
        default:
    }
    JCMethodDecl equalsMethod = createEquals(typeNode, nodesForEquality.toList(), callSuper, fieldAccess, needsCanEqual, source.get(), onParam);
    injectMethod(typeNode, equalsMethod);
    if (needsCanEqual && canEqualExists == MemberExistsResult.NOT_EXISTS) {
        JCMethodDecl canEqualMethod = createCanEqual(typeNode, source.get(), onParam);
        injectMethod(typeNode, canEqualMethod);
    }
    JCMethodDecl hashCodeMethod = createHashCode(typeNode, nodesForEquality.toList(), callSuper, fieldAccess, source.get());
    injectMethod(typeNode, hashCodeMethod);
}
Also used : MemberExistsResult(lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult) JCClassDecl(com.sun.tools.javac.tree.JCTree.JCClassDecl) JCMethodDecl(com.sun.tools.javac.tree.JCTree.JCMethodDecl) EqualsAndHashCode(lombok.EqualsAndHashCode) ListBuffer(com.sun.tools.javac.util.ListBuffer) JCTree(com.sun.tools.javac.tree.JCTree) JCVariableDecl(com.sun.tools.javac.tree.JCTree.JCVariableDecl) JavacNode(lombok.javac.JavacNode) CallSuperType(lombok.core.configuration.CallSuperType)

Example 2 with EqualsAndHashCode

use of lombok.EqualsAndHashCode in project lombok by rzwitserloot.

the class HandleEqualsAndHashCode method handle.

@Override
public void handle(AnnotationValues<EqualsAndHashCode> annotation, Annotation ast, EclipseNode annotationNode) {
    handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode");
    EqualsAndHashCode ann = annotation.getInstance();
    List<String> excludes = Arrays.asList(ann.exclude());
    List<String> includes = Arrays.asList(ann.of());
    EclipseNode typeNode = annotationNode.up();
    List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode);
    checkForBogusFieldNames(typeNode, annotation);
    Boolean callSuper = ann.callSuper();
    if (!annotation.isExplicit("callSuper"))
        callSuper = null;
    if (!annotation.isExplicit("exclude"))
        excludes = null;
    if (!annotation.isExplicit("of"))
        includes = null;
    if (excludes != null && includes != null) {
        excludes = null;
        annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
    }
    Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS);
    boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
    FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
    generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, fieldAccess, onParam);
}
Also used : EqualsAndHashCode(lombok.EqualsAndHashCode) EclipseNode(lombok.eclipse.EclipseNode) FieldAccess(lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess) Annotation(org.eclipse.jdt.internal.compiler.ast.Annotation)

Example 3 with EqualsAndHashCode

use of lombok.EqualsAndHashCode in project lombok by rzwitserloot.

the class HandleEqualsAndHashCode method handle.

@Override
public void handle(AnnotationValues<EqualsAndHashCode> annotation, JCAnnotation ast, JavacNode annotationNode) {
    handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode");
    deleteAnnotationIfNeccessary(annotationNode, EqualsAndHashCode.class);
    EqualsAndHashCode ann = annotation.getInstance();
    List<String> excludes = List.from(ann.exclude());
    List<String> includes = List.from(ann.of());
    JavacNode typeNode = annotationNode.up();
    List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode);
    checkForBogusFieldNames(typeNode, annotation);
    Boolean callSuper = ann.callSuper();
    if (!annotation.isExplicit("callSuper"))
        callSuper = null;
    if (!annotation.isExplicit("exclude"))
        excludes = null;
    if (!annotation.isExplicit("of"))
        includes = null;
    if (excludes != null && includes != null) {
        excludes = null;
        annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
    }
    Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS);
    boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
    FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
    generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, fieldAccess, onParam);
}
Also used : JavacNode(lombok.javac.JavacNode) EqualsAndHashCode(lombok.EqualsAndHashCode) FieldAccess(lombok.javac.handlers.JavacHandlerUtil.FieldAccess) JCAnnotation(com.sun.tools.javac.tree.JCTree.JCAnnotation)

Example 4 with EqualsAndHashCode

use of lombok.EqualsAndHashCode 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)

Aggregations

EqualsAndHashCode (lombok.EqualsAndHashCode)4 CallSuperType (lombok.core.configuration.CallSuperType)2 EclipseNode (lombok.eclipse.EclipseNode)2 JavacNode (lombok.javac.JavacNode)2 JCTree (com.sun.tools.javac.tree.JCTree)1 JCAnnotation (com.sun.tools.javac.tree.JCTree.JCAnnotation)1 JCClassDecl (com.sun.tools.javac.tree.JCTree.JCClassDecl)1 JCMethodDecl (com.sun.tools.javac.tree.JCTree.JCMethodDecl)1 JCVariableDecl (com.sun.tools.javac.tree.JCTree.JCVariableDecl)1 ListBuffer (com.sun.tools.javac.util.ListBuffer)1 ArrayList (java.util.ArrayList)1 FieldAccess (lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess)1 MemberExistsResult (lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult)1 FieldAccess (lombok.javac.handlers.JavacHandlerUtil.FieldAccess)1 MemberExistsResult (lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult)1 Annotation (org.eclipse.jdt.internal.compiler.ast.Annotation)1 FieldDeclaration (org.eclipse.jdt.internal.compiler.ast.FieldDeclaration)1 MethodDeclaration (org.eclipse.jdt.internal.compiler.ast.MethodDeclaration)1 TypeDeclaration (org.eclipse.jdt.internal.compiler.ast.TypeDeclaration)1