/** * When generating a setter, the setter either returns void (beanspec) or Self (fluent). This * method scans for the {@code Accessors} annotation to figure that out. */ public static boolean shouldReturnThis(JavacNode field) { if ((((JCVariableDecl) field.get()).mods.flags & Flags.STATIC) != 0) return false; AnnotationValues<Accessors> accessors = JavacHandlerUtil.getAccessorsForField(field); boolean forced = (accessors.getActualExpression("chain") != null); Accessors instance = accessors.getInstance(); return instance.chain() || (instance.fluent() && !forced); }
public JCMethodDecl createCanEqual(JavacNode typeNode, JCTree source) { /* public boolean canEqual(final java.lang.Object other) { * return other instanceof Outer.Inner.MyType; * } */ JavacTreeMaker maker = typeNode.getTreeMaker(); JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.<JCAnnotation>nil()); JCExpression returnType = maker.TypeIdent(CTC_BOOLEAN); Name canEqualName = typeNode.toName("canEqual"); JCExpression objectType = genJavaLangTypeRef(typeNode, "Object"); Name otherName = typeNode.toName("other"); long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); List<JCVariableDecl> params = List.of(maker.VarDef(maker.Modifiers(flags), otherName, objectType, null)); JCBlock body = maker.Block( 0, List.<JCStatement>of( maker.Return( maker.TypeTest(maker.Ident(otherName), createTypeReference(typeNode))))); return recursiveSetGeneratedBy( maker.MethodDef( mods, canEqualName, returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null), source, typeNode.getContext()); }
public JCMethodDecl createEquals( JavacNode typeNode, List<JavacNode> fields, boolean callSuper, FieldAccess fieldAccess, boolean needsCanEqual, JCTree source) { JavacTreeMaker maker = typeNode.getTreeMaker(); JCClassDecl type = (JCClassDecl) typeNode.get(); Name oName = typeNode.toName("o"); Name otherName = typeNode.toName("other"); Name thisName = typeNode.toName("this"); JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil()); JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation)); JCExpression objectType = genJavaLangTypeRef(typeNode, "Object"); JCExpression returnType = maker.TypeIdent(CTC_BOOLEAN); long finalFlag = JavacHandlerUtil.addFinalIfNeeded(0L, typeNode.getContext()); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); final List<JCVariableDecl> params = List.of( maker.VarDef(maker.Modifiers(finalFlag | Flags.PARAMETER), oName, objectType, null)); /* if (o == this) return true; */ { statements.append( maker.If( maker.Binary(CTC_EQUAL, maker.Ident(oName), maker.Ident(thisName)), returnBool(maker, true), null)); } /* if (!(o instanceof Outer.Inner.MyType) return false; */ { JCUnary notInstanceOf = maker.Unary(CTC_NOT, maker.TypeTest(maker.Ident(oName), createTypeReference(typeNode))); statements.append(maker.If(notInstanceOf, returnBool(maker, false), null)); } /* MyType<?> other = (MyType<?>) o; */ { if (!fields.isEmpty() || needsCanEqual) { final JCExpression selfType1, selfType2; ListBuffer<JCExpression> wildcards1 = new ListBuffer<JCExpression>(); ListBuffer<JCExpression> wildcards2 = new ListBuffer<JCExpression>(); for (int i = 0; i < type.typarams.length(); i++) { wildcards1.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null)); 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.toList()); selfType2 = maker.TypeApply(maker.Ident(type.name), wildcards2.toList()); } statements.append( maker.VarDef( maker.Modifiers(finalFlag), otherName, selfType1, maker.TypeCast(selfType2, maker.Ident(oName)))); } } /* if (!other.canEqual((java.lang.Object) this)) return false; */ { if (needsCanEqual) { List<JCExpression> exprNil = List.nil(); JCExpression thisRef = maker.Ident(thisName); JCExpression castThisRef = maker.TypeCast(genJavaLangTypeRef(typeNode, "Object"), thisRef); JCExpression equalityCheck = maker.Apply( exprNil, maker.Select(maker.Ident(otherName), typeNode.toName("canEqual")), List.of(castThisRef)); statements.append( maker.If(maker.Unary(CTC_NOT, equalityCheck), returnBool(maker, false), null)); } } /* if (!super.equals(o)) return false; */ if (callSuper) { JCMethodInvocation callToSuper = maker.Apply( List.<JCExpression>nil(), maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")), List.<JCExpression>of(maker.Ident(oName))); JCUnary superNotEqual = maker.Unary(CTC_NOT, callToSuper); statements.append(maker.If(superNotEqual, returnBool(maker, false), null)); } Name thisDollar = typeNode.toName("this$"); Name otherDollar = typeNode.toName("other$"); for (JavacNode fieldNode : fields) { JCExpression fType = getFieldType(fieldNode, fieldAccess); JCExpression thisFieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess); JCExpression otherFieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess, maker.Ident(otherName)); if (fType instanceof JCPrimitiveTypeTree) { switch (((JCPrimitiveTypeTree) fType).getPrimitiveTypeKind()) { case FLOAT: /* if (Float.compare(this.fieldName, other.fieldName) != 0) return false; */ statements.append( generateCompareFloatOrDouble( thisFieldAccessor, otherFieldAccessor, maker, typeNode, false)); break; case DOUBLE: /* if (Double.compare(this.fieldName, other.fieldName) != 0) return false; */ statements.append( generateCompareFloatOrDouble( thisFieldAccessor, otherFieldAccessor, maker, typeNode, true)); break; default: /* if (this.fieldName != other.fieldName) return false; */ statements.append( maker.If( maker.Binary(CTC_NOT_EQUAL, thisFieldAccessor, otherFieldAccessor), 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(typeNode, "java", "util", "Arrays", useDeepEquals ? "deepEquals" : "equals"); List<JCExpression> args = List.of(thisFieldAccessor, otherFieldAccessor); statements.append( maker.If( maker.Unary(CTC_NOT, maker.Apply(List.<JCExpression>nil(), eqMethod, args)), returnBool(maker, false), null)); } else /* objects */ { /* final java.lang.Object this$fieldName = this.fieldName; */ /* final java.lang.Object other$fieldName = other.fieldName; */ /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false;; */ Name fieldName = ((JCVariableDecl) fieldNode.get()).name; Name thisDollarFieldName = thisDollar.append(fieldName); Name otherDollarFieldName = otherDollar.append(fieldName); statements.append( maker.VarDef( maker.Modifiers(finalFlag), thisDollarFieldName, genJavaLangTypeRef(typeNode, "Object"), thisFieldAccessor)); statements.append( maker.VarDef( maker.Modifiers(finalFlag), otherDollarFieldName, genJavaLangTypeRef(typeNode, "Object"), otherFieldAccessor)); JCExpression thisEqualsNull = maker.Binary(CTC_EQUAL, maker.Ident(thisDollarFieldName), maker.Literal(CTC_BOT, null)); JCExpression otherNotEqualsNull = maker.Binary( CTC_NOT_EQUAL, maker.Ident(otherDollarFieldName), maker.Literal(CTC_BOT, null)); JCExpression thisEqualsThat = maker.Apply( List.<JCExpression>nil(), maker.Select(maker.Ident(thisDollarFieldName), typeNode.toName("equals")), List.<JCExpression>of(maker.Ident(otherDollarFieldName))); JCExpression fieldsAreNotEqual = maker.Conditional( thisEqualsNull, otherNotEqualsNull, maker.Unary(CTC_NOT, thisEqualsThat)); statements.append(maker.If(fieldsAreNotEqual, returnBool(maker, false), null)); } } /* return true; */ { statements.append(returnBool(maker, true)); } JCBlock body = maker.Block(0, statements.toList()); return recursiveSetGeneratedBy( maker.MethodDef( mods, typeNode.toName("equals"), returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null), source, typeNode.getContext()); }
public JCMethodDecl createHashCode( JavacNode typeNode, List<JavacNode> fields, boolean callSuper, FieldAccess fieldAccess, JCTree source) { JavacTreeMaker maker = typeNode.getTreeMaker(); JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil()); JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation)); JCExpression returnType = maker.TypeIdent(CTC_INT); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); Name primeName = typeNode.toName(PRIME_NAME); Name resultName = typeNode.toName(RESULT_NAME); long finalFlag = JavacHandlerUtil.addFinalIfNeeded(0L, typeNode.getContext()); /* final int PRIME = X; */ { if (!fields.isEmpty() || callSuper) { statements.append( maker.VarDef( maker.Modifiers(finalFlag), primeName, maker.TypeIdent(CTC_INT), maker.Literal(HandlerUtil.primeForHashcode()))); } } /* int result = 1; */ { statements.append( maker.VarDef(maker.Modifiers(0), resultName, maker.TypeIdent(CTC_INT), maker.Literal(1))); } if (callSuper) { JCMethodInvocation callToSuper = maker.Apply( List.<JCExpression>nil(), maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")), List.<JCExpression>nil()); statements.append(createResultCalculation(typeNode, callToSuper)); } Name dollar = typeNode.toName("$"); for (JavacNode fieldNode : fields) { JCExpression fType = getFieldType(fieldNode, fieldAccess); JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess); if (fType instanceof JCPrimitiveTypeTree) { switch (((JCPrimitiveTypeTree) fType).getPrimitiveTypeKind()) { case BOOLEAN: /* this.fieldName ? X : Y */ statements.append( createResultCalculation( typeNode, maker.Conditional( fieldAccessor, maker.Literal(HandlerUtil.primeForTrue()), maker.Literal(HandlerUtil.primeForFalse())))); break; case LONG: { Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name); statements.append( maker.VarDef( maker.Modifiers(finalFlag), dollarFieldName, maker.TypeIdent(CTC_LONG), fieldAccessor)); statements.append( createResultCalculation( typeNode, longToIntForHashCode( maker, maker.Ident(dollarFieldName), maker.Ident(dollarFieldName)))); } break; case FLOAT: /* Float.floatToIntBits(this.fieldName) */ statements.append( createResultCalculation( typeNode, maker.Apply( List.<JCExpression>nil(), genJavaLangTypeRef(typeNode, "Float", "floatToIntBits"), List.of(fieldAccessor)))); break; case DOUBLE: { /* longToIntForHashCode(Double.doubleToLongBits(this.fieldName)) */ Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name); JCExpression init = maker.Apply( List.<JCExpression>nil(), genJavaLangTypeRef(typeNode, "Double", "doubleToLongBits"), List.of(fieldAccessor)); statements.append( maker.VarDef( maker.Modifiers(finalFlag), dollarFieldName, maker.TypeIdent(CTC_LONG), init)); statements.append( createResultCalculation( typeNode, longToIntForHashCode( maker, maker.Ident(dollarFieldName), maker.Ident(dollarFieldName)))); } break; default: case BYTE: case SHORT: case INT: case CHAR: /* just the field */ statements.append(createResultCalculation(typeNode, fieldAccessor)); 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 useDeepHC = multiDim || !primitiveArray; JCExpression hcMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepHC ? "deepHashCode" : "hashCode"); statements.append( createResultCalculation( typeNode, maker.Apply(List.<JCExpression>nil(), hcMethod, List.of(fieldAccessor)))); } else /* objects */ { /* final java.lang.Object $fieldName = this.fieldName; */ /* $fieldName == null ? 0 : $fieldName.hashCode() */ Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name); statements.append( maker.VarDef( maker.Modifiers(finalFlag), dollarFieldName, genJavaLangTypeRef(typeNode, "Object"), fieldAccessor)); JCExpression hcCall = maker.Apply( List.<JCExpression>nil(), maker.Select(maker.Ident(dollarFieldName), typeNode.toName("hashCode")), List.<JCExpression>nil()); JCExpression thisEqualsNull = maker.Binary(CTC_EQUAL, maker.Ident(dollarFieldName), maker.Literal(CTC_BOT, null)); statements.append( createResultCalculation( typeNode, maker.Conditional(thisEqualsNull, maker.Literal(0), hcCall))); } } /* return result; */ { statements.append(maker.Return(maker.Ident(resultName))); } JCBlock body = maker.Block(0, statements.toList()); return recursiveSetGeneratedBy( maker.MethodDef( mods, typeNode.toName("hashCode"), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source, typeNode.getContext()); }