Search in sources :

Example 1 with CallSuperType

use of lombok.core.configuration.CallSuperType 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 CallSuperType

use of lombok.core.configuration.CallSuperType 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)2 CallSuperType (lombok.core.configuration.CallSuperType)2 JCTree (com.sun.tools.javac.tree.JCTree)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 EclipseNode (lombok.eclipse.EclipseNode)1 MemberExistsResult (lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult)1 JavacNode (lombok.javac.JavacNode)1 MemberExistsResult (lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult)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