diff options
Diffstat (limited to 'src/lombok/javac/handlers')
-rw-r--r-- | src/lombok/javac/handlers/HandleData.java | 132 | ||||
-rw-r--r-- | src/lombok/javac/handlers/PKG.java | 14 |
2 files changed, 145 insertions, 1 deletions
diff --git a/src/lombok/javac/handlers/HandleData.java b/src/lombok/javac/handlers/HandleData.java index 8ab50812..ea34cd55 100644 --- a/src/lombok/javac/handlers/HandleData.java +++ b/src/lombok/javac/handlers/HandleData.java @@ -12,10 +12,15 @@ import lombok.javac.JavacAST.Node; import org.mangosdk.spi.ProviderFor; +import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.TypeTags; +import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; import com.sun.tools.javac.tree.JCTree.JCAssign; +import com.sun.tools.javac.tree.JCTree.JCBinary; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; @@ -23,12 +28,14 @@ import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCModifiers; +import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree; import com.sun.tools.javac.tree.JCTree.JCReturn; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeApply; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Name; @ProviderFor(JavacAnnotationHandler.class) public class HandleData implements JavacAnnotationHandler<Data> { @@ -71,10 +78,133 @@ public class HandleData implements JavacAnnotationHandler<Data> { injectMethod(typeNode, staticConstructor); } - //TODO hashCode, equals, toString. + if ( methodExists("equals", typeNode) == MethodExistsResult.NOT_EXISTS ) { + JCMethodDecl method = createEquals(typeNode, nodesForEquality); + injectMethod(typeNode, method); + } + + //TODO hashCode, toString. return true; } + private JCMethodDecl createEquals(Node typeNode, List<Node> fields) { + TreeMaker maker = typeNode.getTreeMaker(); + JCClassDecl type = (JCClassDecl) typeNode.get(); + + Name oName = typeNode.toName("o"); + Name otherName = typeNode.toName("other"); + Name thisName = typeNode.toName("this"); + + JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCExpression objectType = maker.Type(typeNode.getSymbolTable().objectType); + JCExpression returnType = maker.TypeIdent(TypeTags.BOOLEAN); + + List<JCStatement> statements = List.nil(); + List<JCVariableDecl> params = List.of(maker.VarDef(maker.Modifiers(0), oName, objectType, null)); + + /* if ( o == this ) return true; */ { + statements = statements.append( + maker.If(maker.Binary(JCTree.EQ, maker.Ident(oName), maker.Ident(thisName)), returnBool(maker, true), null)); + } + + /* if ( o == null ) return false; */ { + statements = statements.append( + maker.If(maker.Binary(JCTree.EQ, maker.Ident(oName), maker.Literal(TypeTags.BOT, null)), returnBool(maker, false), null)); + } + + /* if ( o.getClass() != this.getClass() ) return false; */ { + Name getClass = typeNode.toName("getClass"); + List<JCExpression> exprNil = List.nil(); + JCExpression oGetClass = maker.Apply(exprNil, maker.Select(maker.Ident(oName), getClass), exprNil); + JCExpression thisGetClass = maker.Apply(exprNil, maker.Select(maker.Ident(thisName), getClass), exprNil); + statements = statements.append( + maker.If(maker.Binary(JCTree.NE, oGetClass, thisGetClass), returnBool(maker, false), null)); + } + + /* MyType<?> other = (MyType<?>) o; */ { + final JCExpression selfType1, selfType2; + List<JCExpression> wildcards1 = List.nil(); + List<JCExpression> wildcards2 = List.nil(); + for ( int i = 0 ; i < type.typarams.length() ; i++ ) { + wildcards1 = wildcards1.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null)); + wildcards2 = wildcards2.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null)); + } + + if ( type.typarams.isEmpty() ) { + selfType1 = maker.Ident(type.name); + selfType2 = maker.Ident(type.name); + } else { + selfType1 = maker.TypeApply(maker.Ident(type.name), wildcards1); + selfType2 = maker.TypeApply(maker.Ident(type.name), wildcards2); + } + + statements = statements.append( + maker.VarDef(maker.Modifiers(Flags.FINAL), otherName, selfType1, maker.TypeCast(selfType2, maker.Ident(oName)))); + } + + for ( Node fieldNode : fields ) { + JCVariableDecl field = (JCVariableDecl) fieldNode.get(); + JCExpression fType = field.vartype; + JCExpression thisDotField = maker.Select(maker.Ident(thisName), field.name); + JCExpression otherDotField = maker.Select(maker.Ident(otherName), field.name); + if ( fType instanceof JCPrimitiveTypeTree ) { + switch ( ((JCPrimitiveTypeTree)fType).getPrimitiveTypeKind() ) { + case FLOAT: + /* if ( Float.compare(this.fieldName, other.fieldName) != 0 ) return false; */ + statements = statements.append(generateCompareFloatOrDouble(thisDotField, otherDotField, maker, typeNode, false)); + break; + case DOUBLE: + /* if ( Double(this.fieldName, other.fieldName) != 0 ) return false; */ + statements = statements.append(generateCompareFloatOrDouble(thisDotField, otherDotField, maker, typeNode, true)); + break; + default: + /* if ( this.fieldName != other.fieldName ) return false; */ + statements = statements.append( + maker.If(maker.Binary(JCTree.NE, thisDotField, otherDotField), returnBool(maker, false), null)); + break; + } + } else if ( fType instanceof JCArrayTypeTree ) { + /* if ( !java.util.Arrays.deepEquals(this.fieldName, other.fieldName) ) return false; //use equals for primitive arrays. */ + boolean multiDim = ((JCArrayTypeTree)fType).elemtype instanceof JCArrayTypeTree; + boolean primitiveArray = ((JCArrayTypeTree)fType).elemtype instanceof JCPrimitiveTypeTree; + boolean useDeepEquals = multiDim || !primitiveArray; + + JCExpression eqMethod = chainDots(maker, typeNode, "java", "util", "Arrays", useDeepEquals ? "deepEquals" : "equals"); + List<JCExpression> args = List.of(thisDotField, otherDotField); + statements = statements.append(maker.If(maker.Unary(JCTree.NOT, + maker.Apply(List.<JCExpression>nil(), eqMethod, args)), returnBool(maker, false), null)); + } else /* objects */ { + /* if ( this.fieldName == null ? other.fieldName != null : !this.fieldName.equals(other.fieldName) ) return false; */ + JCExpression thisEqualsNull = maker.Binary(JCTree.EQ, thisDotField, maker.Literal(TypeTags.BOT, null)); + JCExpression otherNotEqualsNull = maker.Binary(JCTree.NE, otherDotField, maker.Literal(TypeTags.BOT, null)); + JCExpression thisEqualsThat = maker.Apply( + List.<JCExpression>nil(), maker.Select(thisDotField, typeNode.toName("equals")), List.of(otherDotField)); + JCExpression fieldsAreNotEqual = maker.Conditional(thisEqualsNull, otherNotEqualsNull, maker.Unary(JCTree.NOT, thisEqualsThat)); + statements = statements.append(maker.If(fieldsAreNotEqual, returnBool(maker, false), null)); + } + } + + /* return true; */ { + statements = statements.append(returnBool(maker, true)); + } + + JCBlock body = maker.Block(0, statements); + return maker.MethodDef(mods, typeNode.toName("equals"), returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null); + } + + private JCStatement generateCompareFloatOrDouble(JCExpression thisDotField, JCExpression otherDotField, TreeMaker maker, Node node, boolean isDouble) { + /* if ( Float.compare(fieldName, other.fieldName) != 0 ) return false; */ + JCExpression clazz = chainDots(maker, node, "java", "lang", isDouble ? "Double" : "Float"); + List<JCExpression> args = List.of(thisDotField, otherDotField); + JCBinary compareCallEquals0 = maker.Binary(JCTree.NE, maker.Apply( + List.<JCExpression>nil(), maker.Select(clazz, node.toName("compare")), args), maker.Literal(0)); + return maker.If(compareCallEquals0, returnBool(maker, false), null); + } + + private JCStatement returnBool(TreeMaker maker, boolean bool) { + return maker.Return(maker.Literal(TypeTags.BOOLEAN, bool ? 1 : 0)); + } + private JCMethodDecl createConstructor(boolean isPublic, Node typeNode, List<Node> fields) { TreeMaker maker = typeNode.getTreeMaker(); JCClassDecl type = (JCClassDecl) typeNode.get(); diff --git a/src/lombok/javac/handlers/PKG.java b/src/lombok/javac/handlers/PKG.java index 25d1a93e..76bdd527 100644 --- a/src/lombok/javac/handlers/PKG.java +++ b/src/lombok/javac/handlers/PKG.java @@ -4,7 +4,9 @@ import java.lang.reflect.Modifier; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.List; @@ -123,4 +125,16 @@ class PKG { } return out; } + + static JCExpression chainDots(TreeMaker maker, JavacAST.Node node, String... elems) { + assert elems != null; + assert elems.length > 0; + + JCExpression e = maker.Ident(node.toName(elems[0])); + for ( int i = 1 ; i < elems.length ; i++ ) { + e = maker.Select(e, node.toName(elems[i])); + } + + return e; + } } |