diff options
Diffstat (limited to 'src/lombok')
-rw-r--r-- | src/lombok/javac/handlers/HandleData.java | 119 |
1 files changed, 117 insertions, 2 deletions
diff --git a/src/lombok/javac/handlers/HandleData.java b/src/lombok/javac/handlers/HandleData.java index ea34cd55..2faf96fd 100644 --- a/src/lombok/javac/handlers/HandleData.java +++ b/src/lombok/javac/handlers/HandleData.java @@ -83,10 +83,124 @@ public class HandleData implements JavacAnnotationHandler<Data> { injectMethod(typeNode, method); } - //TODO hashCode, toString. + if ( methodExists("hashCode", typeNode) == MethodExistsResult.NOT_EXISTS ) { + JCMethodDecl method = createHashCode(typeNode, nodesForEquality); + injectMethod(typeNode, method); + } + + //TODO toString. return true; } + private JCMethodDecl createHashCode(Node typeNode, List<Node> fields) { + TreeMaker maker = typeNode.getTreeMaker(); + + JCAnnotation overrideAnnotation = maker.Annotation(chainDots(maker, typeNode, "java", "lang", "Override"), List.<JCExpression>nil()); + JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation)); + JCExpression returnType = maker.TypeIdent(TypeTags.INT); + List<JCStatement> statements = List.nil(); + + Name primeName = typeNode.toName("PRIME"); + Name resultName = typeNode.toName("result"); + /* final int PRIME = 31; */ { + if ( !fields.isEmpty() ) { + statements = statements.append( + maker.VarDef(maker.Modifiers(Flags.FINAL), primeName, maker.TypeIdent(TypeTags.INT), maker.Literal(31))); + } + } + + /* int result = 1; */ { + statements = statements.append(maker.VarDef(maker.Modifiers(0), resultName, maker.TypeIdent(TypeTags.INT), maker.Literal(1))); + } + + List<JCExpression> intoResult = List.nil(); + + int tempCounter = 0; + for ( Node fieldNode : fields ) { + JCVariableDecl field = (JCVariableDecl) fieldNode.get(); + JCExpression fType = field.vartype; + JCExpression thisDotField = maker.Select(maker.Ident(typeNode.toName("this")), field.name); + JCExpression thisDotFieldClone = maker.Select(maker.Ident(typeNode.toName("this")), field.name); + if ( fType instanceof JCPrimitiveTypeTree ) { + switch ( ((JCPrimitiveTypeTree)fType).getPrimitiveTypeKind() ) { + case BOOLEAN: + /* this.fieldName ? 1231 : 1237 */ + intoResult = intoResult.append(maker.Conditional(thisDotField, maker.Literal(1231), maker.Literal(1237))); + break; + case LONG: + intoResult = intoResult.append(longToIntForHashCode(maker, thisDotField, thisDotFieldClone)); + break; + case FLOAT: + /* Float.floatToIntBits(this.fieldName) */ + intoResult = intoResult.append(maker.Apply( + List.<JCExpression>nil(), + chainDots(maker, typeNode, "java", "lang", "Float", "floatToIntBits"), + List.of(thisDotField))); + break; + case DOUBLE: + /* longToIntForHashCode(Double.doubleToLongBits(this.fieldName)) */ + Name tempVar = typeNode.toName("temp" + (++tempCounter)); + JCExpression init = maker.Apply( + List.<JCExpression>nil(), + chainDots(maker, typeNode, "java", "lang", "Double", "doubleToLongBits"), + List.of(thisDotField)); + statements = statements.append( + maker.VarDef(maker.Modifiers(Flags.FINAL), tempVar, maker.TypeIdent(TypeTags.LONG), init)); + intoResult = intoResult.append(longToIntForHashCode(maker, maker.Ident(tempVar), maker.Ident(tempVar))); + break; + default: + case BYTE: + case SHORT: + case INT: + case CHAR: + /* just the field */ + intoResult = intoResult.append(thisDotField); + break; + } + } else if ( fType instanceof JCArrayTypeTree ) { + /* java.util.Arrays.deepHashCode(this.fieldName) //use just hashCode() for primitive arrays. */ + boolean multiDim = ((JCArrayTypeTree)fType).elemtype instanceof JCArrayTypeTree; + boolean primitiveArray = ((JCArrayTypeTree)fType).elemtype instanceof JCPrimitiveTypeTree; + boolean useDeepEquals = multiDim || !primitiveArray; + + JCExpression hcMethod = chainDots(maker, typeNode, "java", "util", "Arrays", useDeepEquals ? "deepHashCode" : "hashCode"); + intoResult = intoResult.append( + maker.Apply(List.<JCExpression>nil(), hcMethod, List.of(thisDotField))); + } else /* objects */ { + /* this.fieldName == null ? 0 : this.fieldName.hashCode() */ + JCExpression hcCall = maker.Apply(List.<JCExpression>nil(), maker.Select(thisDotField, typeNode.toName("hashCode")), + List.<JCExpression>nil()); + JCExpression thisEqualsNull = maker.Binary(JCTree.EQ, thisDotField, maker.Literal(TypeTags.BOT, null)); + intoResult = intoResult.append( + maker.Conditional(thisEqualsNull, maker.Literal(0), hcCall)); + } + } + + /* fold each intoResult entry into: + result = result * PRIME + (item); */ + for ( JCExpression expr : intoResult ) { + JCExpression mult = maker.Binary(JCTree.MUL, maker.Ident(resultName), maker.Ident(primeName)); + JCExpression add = maker.Binary(JCTree.PLUS, mult, expr); + statements = statements.append(maker.Exec(maker.Assign(maker.Ident(resultName), add))); + } + + /* return result; */ { + statements = statements.append(maker.Return(maker.Ident(resultName))); + } + + JCBlock body = maker.Block(0, statements); + return maker.MethodDef(mods, typeNode.toName("hashCode"), returnType, + List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); + } + + /** The 2 references must be clones of each other. */ + private JCExpression longToIntForHashCode(TreeMaker maker, JCExpression ref1, JCExpression ref2) { + /* (int)(ref >>> 32 ^ ref) */ + JCExpression shift = maker.Binary(JCTree.USR, ref1, maker.Literal(32)); + JCExpression xorBits = maker.Binary(JCTree.BITXOR, shift, ref2); + return maker.TypeCast(maker.TypeIdent(TypeTags.INT), xorBits); + } + private JCMethodDecl createEquals(Node typeNode, List<Node> fields) { TreeMaker maker = typeNode.getTreeMaker(); JCClassDecl type = (JCClassDecl) typeNode.get(); @@ -95,7 +209,8 @@ public class HandleData implements JavacAnnotationHandler<Data> { Name otherName = typeNode.toName("other"); Name thisName = typeNode.toName("this"); - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCAnnotation overrideAnnotation = maker.Annotation(chainDots(maker, typeNode, "java", "lang", "Override"), List.<JCExpression>nil()); + JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation)); JCExpression objectType = maker.Type(typeNode.getSymbolTable().objectType); JCExpression returnType = maker.TypeIdent(TypeTags.BOOLEAN); |