diff options
author | Reinier Zwitserloot <reinier@tipit.to> | 2009-07-27 11:04:10 +0200 |
---|---|---|
committer | Reinier Zwitserloot <reinier@tipit.to> | 2009-07-27 11:04:10 +0200 |
commit | 8fbcf8ffe43a9097b48d130caeaa3caba07063e6 (patch) | |
tree | ae7c79e1d21f9c64666c1308518eda62b6ac1e30 /src/lombok/eclipse/handlers/HandleData.java | |
parent | beb0213b012cde40cf839703e9103a9aad4ebddc (diff) | |
download | lombok-8fbcf8ffe43a9097b48d130caeaa3caba07063e6.tar.gz lombok-8fbcf8ffe43a9097b48d130caeaa3caba07063e6.tar.bz2 lombok-8fbcf8ffe43a9097b48d130caeaa3caba07063e6.zip |
Split off generation of equals() and hashCode() methods form @Data into a new annotation, @EqualsAndHashCode.
Addresses issue #8
Diffstat (limited to 'src/lombok/eclipse/handlers/HandleData.java')
-rw-r--r-- | src/lombok/eclipse/handlers/HandleData.java | 336 |
1 files changed, 1 insertions, 335 deletions
diff --git a/src/lombok/eclipse/handlers/HandleData.java b/src/lombok/eclipse/handlers/HandleData.java index 749418eb..f1623e39 100644 --- a/src/lombok/eclipse/handlers/HandleData.java +++ b/src/lombok/eclipse/handlers/HandleData.java @@ -26,12 +26,8 @@ import static lombok.eclipse.handlers.PKG.*; import java.lang.reflect.Modifier; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; import lombok.AccessLevel; import lombok.Data; @@ -47,44 +43,23 @@ import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Assignment; -import org.eclipse.jdt.internal.compiler.ast.BinaryExpression; -import org.eclipse.jdt.internal.compiler.ast.CastExpression; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; -import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; -import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.Expression; -import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldReference; -import org.eclipse.jdt.internal.compiler.ast.IfStatement; -import org.eclipse.jdt.internal.compiler.ast.IntLiteral; -import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; -import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation; -import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; -import org.eclipse.jdt.internal.compiler.ast.NameReference; -import org.eclipse.jdt.internal.compiler.ast.NullLiteral; -import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; -import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; -import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; -import org.eclipse.jdt.internal.compiler.ast.Reference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.ThisReference; -import org.eclipse.jdt.internal.compiler.ast.TrueLiteral; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference; -import org.eclipse.jdt.internal.compiler.ast.UnaryExpression; -import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; -import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.mangosdk.spi.ProviderFor; /** @@ -107,14 +82,12 @@ public class HandleData implements EclipseAnnotationHandler<Data> { return false; } - List<Node> nodesForEquality = new ArrayList<Node>(); List<Node> nodesForConstructor = new ArrayList<Node>(); for ( Node child : typeNode.down() ) { if ( child.getKind() != Kind.FIELD ) continue; FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); //Skip static fields. if ( (fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0 ) continue; - if ( (fieldDecl.modifiers & ClassFileConstants.AccTransient) == 0 ) nodesForEquality.add(child); boolean isFinal = (fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0; if ( isFinal && fieldDecl.initialization == null ) nodesForConstructor.add(child); new HandleGetter().generateGetterForField(child, annotationNode.get()); @@ -122,16 +95,7 @@ public class HandleData implements EclipseAnnotationHandler<Data> { } new HandleToString().generateToStringForType(typeNode, annotationNode); - - if ( methodExists("equals", typeNode) == MemberExistsResult.NOT_EXISTS ) { - MethodDeclaration equals = createEquals(typeNode, nodesForEquality, ast); - injectMethod(typeNode, equals); - } - - if ( methodExists("hashCode", typeNode) == MemberExistsResult.NOT_EXISTS ) { - MethodDeclaration hashCode = createHashCode(typeNode, nodesForEquality, ast); - injectMethod(typeNode, hashCode); - } + new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode); //Careful: Generate the public static constructor (if there is one) LAST, so that any attempt to //'find callers' on the annotation node will find callers of the constructor, which is by far the @@ -232,302 +196,4 @@ public class HandleData implements EclipseAnnotationHandler<Data> { constructor.statements = new Statement[] { new ReturnStatement(statement, (int)(p >> 32), (int)p) }; return constructor; } - - private static final Set<String> BUILT_IN_TYPES = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList( - "byte", "short", "int", "long", "char", "boolean", "double", "float"))); - - private MethodDeclaration createEquals(Node type, Collection<Node> fields, ASTNode pos) { - MethodDeclaration method = new MethodDeclaration( - ((CompilationUnitDeclaration) type.top().get()).compilationResult); - - method.modifiers = PKG.toModifier(AccessLevel.PUBLIC); - method.returnType = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); - method.annotations = new Annotation[] { - new MarkerAnnotation(new QualifiedTypeReference(TypeConstants.JAVA_LANG_OVERRIDE, new long[] { 0, 0, 0}), 0) - }; - method.selector = "equals".toCharArray(); - method.thrownExceptions = null; - method.typeParameters = null; - method.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; - method.bodyStart = method.declarationSourceStart = method.sourceStart = pos.sourceStart; - method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = pos.sourceEnd; - method.arguments = new Argument[] { - new Argument(new char[] { 'o' }, 0, - new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, new long[] { 0, 0, 0 }), 0) - }; - - List<Statement> statements = new ArrayList<Statement>(); - - /* if ( o == this ) return true; */ { - EqualExpression otherEqualsThis = new EqualExpression( - new SingleNameReference(new char[] { 'o' }, 0), - new ThisReference(0, 0), OperatorIds.EQUAL_EQUAL); - - ReturnStatement returnTrue = new ReturnStatement(new TrueLiteral(0, 0), 0, 0); - IfStatement ifOtherEqualsThis = new IfStatement(otherEqualsThis, returnTrue, 0, 0); - statements.add(ifOtherEqualsThis); - } - - /* if ( o == null ) return false; */ { - EqualExpression otherEqualsNull = new EqualExpression( - new SingleNameReference(new char[] { 'o' }, 0), - new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL); - - ReturnStatement returnFalse = new ReturnStatement(new FalseLiteral(0, 0), 0, 0); - IfStatement ifOtherEqualsNull = new IfStatement(otherEqualsNull, returnFalse, 0, 0); - statements.add(ifOtherEqualsNull); - } - - /* if ( o.getClass() != getClass() ) return false; */ { - MessageSend otherGetClass = new MessageSend(); - otherGetClass.receiver = new SingleNameReference(new char[] { 'o' }, 0); - otherGetClass.selector = "getClass".toCharArray(); - MessageSend thisGetClass = new MessageSend(); - thisGetClass.receiver = new ThisReference(0, 0); - thisGetClass.selector = "getClass".toCharArray(); - EqualExpression classesNotEqual = new EqualExpression(otherGetClass, thisGetClass, OperatorIds.NOT_EQUAL); - ReturnStatement returnFalse = new ReturnStatement(new FalseLiteral(0, 0), 0, 0); - IfStatement ifClassesNotEqual = new IfStatement(classesNotEqual, returnFalse, 0, 0); - statements.add(ifClassesNotEqual); - } - - char[] otherN = "other".toCharArray(); - - - TypeDeclaration typeDecl = (TypeDeclaration)type.get(); - /* MyType<?> other = (MyType<?>) o; */ { - if ( !fields.isEmpty() ) { - LocalDeclaration other = new LocalDeclaration(otherN, 0, 0); - char[] typeName = typeDecl.name; - Expression targetType; - if ( typeDecl.typeParameters == null || typeDecl.typeParameters.length == 0 ) { - targetType = new SingleNameReference(((TypeDeclaration)type.get()).name, 0); - other.type = new SingleTypeReference(typeName, 0); - } else { - TypeReference[] typeArgs = new TypeReference[typeDecl.typeParameters.length]; - for ( int i = 0 ; i < typeArgs.length ; i++ ) typeArgs[i] = new Wildcard(Wildcard.UNBOUND); - targetType = new ParameterizedSingleTypeReference(typeName, typeArgs, 0, 0); - other.type = new ParameterizedSingleTypeReference(typeName, copyTypes(typeArgs), 0, 0); - } - other.initialization = new CastExpression( - new SingleNameReference(new char[] { 'o' }, 0), - targetType); - statements.add(other); - } - } - - for ( Node field : fields ) { - FieldDeclaration f = (FieldDeclaration) field.get(); - char[] token = f.type.getLastToken(); - if ( f.type.dimensions() == 0 && token != null ) { - if ( Arrays.equals(TypeConstants.FLOAT, token) ) { - statements.add(generateCompareFloatOrDouble(otherN, "Float".toCharArray(), f.name)); - } else if ( Arrays.equals(TypeConstants.DOUBLE, token) ) { - statements.add(generateCompareFloatOrDouble(otherN, "Double".toCharArray(), f.name)); - } else if ( BUILT_IN_TYPES.contains(new String(token)) ) { - EqualExpression fieldsNotEqual = new EqualExpression( - new SingleNameReference(f.name, 0), - generateQualifiedNameRef(otherN, f.name), - OperatorIds.NOT_EQUAL); - ReturnStatement returnStatement = new ReturnStatement(new FalseLiteral(0, 0), 0, 0); - statements.add(new IfStatement(fieldsNotEqual, returnStatement, 0, 0)); - } else /* objects */ { - EqualExpression fieldIsNull = new EqualExpression( - new SingleNameReference(f.name, 0), - new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL); - EqualExpression otherFieldIsntNull = new EqualExpression( - generateQualifiedNameRef(otherN, f.name), - new NullLiteral(0, 0), OperatorIds.NOT_EQUAL); - MessageSend equalsCall = new MessageSend(); - equalsCall.receiver = new SingleNameReference(f.name, 0); - equalsCall.selector = "equals".toCharArray(); - equalsCall.arguments = new Expression[] { generateQualifiedNameRef(otherN, f.name) }; - UnaryExpression fieldsNotEqual = new UnaryExpression(equalsCall, OperatorIds.NOT); - ConditionalExpression fullEquals = new ConditionalExpression(fieldIsNull, otherFieldIsntNull, fieldsNotEqual); - ReturnStatement returnStatement = new ReturnStatement(new FalseLiteral(0, 0), 0, 0); - statements.add(new IfStatement(fullEquals, returnStatement, 0, 0)); - } - } else if ( f.type.dimensions() > 0 && token != null ) { - MessageSend arraysEqualCall = new MessageSend(); - arraysEqualCall.receiver = generateQualifiedNameRef(TypeConstants.JAVA, TypeConstants.UTIL, "Arrays".toCharArray()); - if ( f.type.dimensions() > 1 || !BUILT_IN_TYPES.contains(new String(token)) ) { - arraysEqualCall.selector = "deepEquals".toCharArray(); - } else { - arraysEqualCall.selector = "equals".toCharArray(); - } - arraysEqualCall.arguments = new Expression[] { - new SingleNameReference(f.name, 0), - generateQualifiedNameRef(otherN, f.name) }; - UnaryExpression arraysNotEqual = new UnaryExpression(arraysEqualCall, OperatorIds.NOT); - ReturnStatement returnStatement = new ReturnStatement(new FalseLiteral(0, 0), 0, 0); - statements.add(new IfStatement(arraysNotEqual, returnStatement, 0, 0)); - } - } - - /* return true; */ { - statements.add(new ReturnStatement(new TrueLiteral(0, 0), 0, 0)); - } - method.statements = statements.toArray(new Statement[statements.size()]); - return method; - } - - private IfStatement generateCompareFloatOrDouble(char[] otherN, char[] floatOrDouble, char[] fieldName) { - /* if ( Float.compare(fieldName, other.fieldName) != 0 ) return false */ - MessageSend floatCompare = new MessageSend(); - floatCompare.receiver = generateQualifiedNameRef(TypeConstants.JAVA, TypeConstants.LANG, floatOrDouble); - floatCompare.selector = "compare".toCharArray(); - floatCompare.arguments = new Expression[] { - new SingleNameReference(fieldName, 0), - generateQualifiedNameRef(otherN, fieldName) - }; - EqualExpression ifFloatCompareIsNot0 = new EqualExpression(floatCompare, new IntLiteral(new char[] {'0'}, 0, 0), OperatorIds.NOT_EQUAL); - ReturnStatement returnFalse = new ReturnStatement(new FalseLiteral(0, 0), 0, 0); - return new IfStatement(ifFloatCompareIsNot0, returnFalse, 0, 0); - } - - private Reference generateFieldReference(char[] fieldName) { - FieldReference thisX = new FieldReference(("this." + new String(fieldName)).toCharArray(), 0); - thisX.receiver = new ThisReference(0, 0); - thisX.token = fieldName; - return thisX; - } - - private NameReference generateQualifiedNameRef(char[]... varNames) { - if ( varNames.length > 1 ) - return new QualifiedNameReference(varNames, new long[varNames.length], 0, 0); - else return new SingleNameReference(varNames[0], 0); - } - - private MethodDeclaration createHashCode(Node type, Collection<Node> fields, ASTNode pos) { - MethodDeclaration method = new MethodDeclaration( - ((CompilationUnitDeclaration) type.top().get()).compilationResult); - - method.modifiers = PKG.toModifier(AccessLevel.PUBLIC); - method.returnType = TypeReference.baseTypeReference(TypeIds.T_int, 0); - method.annotations = new Annotation[] { - new MarkerAnnotation(new QualifiedTypeReference(TypeConstants.JAVA_LANG_OVERRIDE, new long[] { 0, 0, 0}), 0) - }; - method.selector = "hashCode".toCharArray(); - method.thrownExceptions = null; - method.typeParameters = null; - method.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; - method.bodyStart = method.declarationSourceStart = method.sourceStart = pos.sourceStart; - method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = pos.sourceEnd; - method.arguments = null; - - List<Statement> statements = new ArrayList<Statement>(); - List<Expression> intoResult = new ArrayList<Expression>(); - - final char[] PRIME = "PRIME".toCharArray(); - final char[] RESULT = "result".toCharArray(); - final boolean isEmpty = fields.isEmpty(); - - /* final int PRIME = 31; */ { - if ( !isEmpty ) { /* Without fields, PRIME isn't used, and that would trigger a 'local variable not used' warning. */ - LocalDeclaration primeDecl = new LocalDeclaration(PRIME, 0 ,0); - primeDecl.modifiers = Modifier.FINAL; - primeDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0); - primeDecl.initialization = new IntLiteral("31".toCharArray(), 0, 0); - statements.add(primeDecl); - } - } - - /* int result = 1; */ { - LocalDeclaration resultDecl = new LocalDeclaration(RESULT, 0, 0); - resultDecl.initialization = new IntLiteral("1".toCharArray(), 0, 0); - resultDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0); - statements.add(resultDecl); - } - - int tempCounter = 0; - for ( Node field : fields ) { - FieldDeclaration f = (FieldDeclaration) field.get(); - char[] token = f.type.getLastToken(); - if ( f.type.dimensions() == 0 && token != null ) { - if ( Arrays.equals(TypeConstants.FLOAT, token) ) { - /* Float.floatToIntBits(fieldName) */ - MessageSend floatToIntBits = new MessageSend(); - floatToIntBits.receiver = generateQualifiedNameRef(TypeConstants.JAVA_LANG_FLOAT); - floatToIntBits.selector = "floatToIntBits".toCharArray(); - floatToIntBits.arguments = new Expression[] { generateFieldReference(f.name) }; - intoResult.add(floatToIntBits); - } else if ( Arrays.equals(TypeConstants.DOUBLE, token) ) { - /* longToIntForHashCode(Double.doubleToLongBits(fieldName)) */ - MessageSend doubleToLongBits = new MessageSend(); - doubleToLongBits.receiver = generateQualifiedNameRef(TypeConstants.JAVA_LANG_DOUBLE); - doubleToLongBits.selector = "doubleToLongBits".toCharArray(); - doubleToLongBits.arguments = new Expression[] { generateFieldReference(f.name) }; - final char[] tempName = ("temp" + ++tempCounter).toCharArray(); - LocalDeclaration tempVar = new LocalDeclaration(tempName, 0, 0); - tempVar.initialization = doubleToLongBits; - tempVar.type = TypeReference.baseTypeReference(TypeIds.T_long, 0); - tempVar.modifiers = Modifier.FINAL; - statements.add(tempVar); - intoResult.add(longToIntForHashCode( - new SingleNameReference(tempName, 0), new SingleNameReference(tempName, 0))); - } else if ( Arrays.equals(TypeConstants.BOOLEAN, token) ) { - /* booleanField ? 1231 : 1237 */ - intoResult.add(new ConditionalExpression( - generateFieldReference(f.name), - new IntLiteral("1231".toCharArray(), 0, 0), - new IntLiteral("1237".toCharArray(), 0 ,0))); - } else if ( Arrays.equals(TypeConstants.LONG, token) ) { - intoResult.add(longToIntForHashCode(generateFieldReference(f.name), generateFieldReference(f.name))); - } else if ( BUILT_IN_TYPES.contains(new String(token)) ) { - intoResult.add(generateFieldReference(f.name)); - } else /* objects */ { - /* this.fieldName == null ? 0 : this.fieldName.hashCode() */ - MessageSend hashCodeCall = new MessageSend(); - hashCodeCall.receiver = generateFieldReference(f.name); - hashCodeCall.selector = "hashCode".toCharArray(); - EqualExpression objIsNull = new EqualExpression( - generateFieldReference(f.name), - new NullLiteral(0, 0), - OperatorIds.EQUAL_EQUAL); - ConditionalExpression nullOrHashCode = new ConditionalExpression( - objIsNull, - new IntLiteral("0".toCharArray(), 0, 0), - hashCodeCall); - intoResult.add(nullOrHashCode); - } - } else if ( f.type.dimensions() > 0 && token != null ) { - /* Arrays.deepHashCode(array) //just hashCode for simple arrays */ - MessageSend arraysHashCodeCall = new MessageSend(); - arraysHashCodeCall.receiver = generateQualifiedNameRef(TypeConstants.JAVA, TypeConstants.UTIL, "Arrays".toCharArray()); - if ( f.type.dimensions() > 1 || !BUILT_IN_TYPES.contains(new String(token)) ) { - arraysHashCodeCall.selector = "deepHashCode".toCharArray(); - } else { - arraysHashCodeCall.selector = "hashCode".toCharArray(); - } - arraysHashCodeCall.arguments = new Expression[] { generateFieldReference(f.name) }; - intoResult.add(arraysHashCodeCall); - } - } - - /* fold each intoResult entry into: - result = result * PRIME + (item); */ { - for ( Expression ex : intoResult ) { - BinaryExpression multiplyByPrime = new BinaryExpression(new SingleNameReference(RESULT, 0), - new SingleNameReference(PRIME, 0), OperatorIds.MULTIPLY); - BinaryExpression addItem = new BinaryExpression(multiplyByPrime, ex, OperatorIds.PLUS); - statements.add(new Assignment(new SingleNameReference(RESULT, 0), addItem, 0)); - } - } - - /* return result; */ { - statements.add(new ReturnStatement(new SingleNameReference(RESULT, 0), 0, 0)); - } - method.statements = statements.toArray(new Statement[statements.size()]); - return method; - } - - /** Give 2 clones! */ - private Expression longToIntForHashCode(Reference ref1, Reference ref2) { - /* (int)(ref >>> 32 ^ ref) */ - BinaryExpression higherBits = new BinaryExpression( - ref1, new IntLiteral("32".toCharArray(), 0, 0), - OperatorIds.UNSIGNED_RIGHT_SHIFT); - BinaryExpression xorParts = new BinaryExpression(ref2, higherBits, OperatorIds.XOR); - return new CastExpression(xorParts, TypeReference.baseTypeReference(TypeIds.T_int, 0)); - } } |