Repository: groovy Updated Branches: refs/heads/master 011b98e13 -> e7aeae058
http://git-wip-us.apache.org/repos/asf/groovy/blob/69fc7ab3/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java index 5fb6874..ad4001f 100644 --- a/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java +++ b/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java @@ -20,9 +20,8 @@ package org.codehaus.groovy.transform; import groovy.lang.MetaClass; import groovy.lang.MissingPropertyException; -import groovy.lang.ReadOnlyPropertyException; import groovy.transform.ImmutableBase; -import groovy.transform.KnownImmutable; +import org.apache.groovy.ast.tools.ImmutablePropertyUtils; import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.AnnotatedNode; import org.codehaus.groovy.ast.AnnotationNode; @@ -31,42 +30,35 @@ import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.ConstructorNode; import org.codehaus.groovy.ast.FieldNode; -import org.codehaus.groovy.ast.GenericsType; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.PropertyNode; import org.codehaus.groovy.ast.VariableScope; -import org.codehaus.groovy.ast.expr.ArrayExpression; -import org.codehaus.groovy.ast.expr.ClassExpression; import org.codehaus.groovy.ast.expr.ConstantExpression; import org.codehaus.groovy.ast.expr.Expression; -import org.codehaus.groovy.ast.expr.ListExpression; import org.codehaus.groovy.ast.expr.MapExpression; import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.ast.stmt.BlockStatement; -import org.codehaus.groovy.ast.stmt.EmptyStatement; import org.codehaus.groovy.ast.stmt.Statement; import org.codehaus.groovy.control.CompilePhase; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.codehaus.groovy.runtime.InvokerHelper; -import org.codehaus.groovy.runtime.ReflectionMethodInvoker; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.Date; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.SortedMap; -import java.util.SortedSet; -import static org.codehaus.groovy.ast.ClassHelper.make; +import static org.apache.groovy.ast.tools.ImmutablePropertyUtils.builtinOrMarkedImmutableClass; +import static org.apache.groovy.ast.tools.ImmutablePropertyUtils.cloneArrayOrCloneableExpr; +import static org.apache.groovy.ast.tools.ImmutablePropertyUtils.cloneDateExpr; +import static org.apache.groovy.ast.tools.ImmutablePropertyUtils.createErrorMessage; +import static org.apache.groovy.ast.tools.ImmutablePropertyUtils.derivesFromDate; +import static org.apache.groovy.ast.tools.ImmutablePropertyUtils.getKnownImmutables; +import static org.apache.groovy.ast.tools.ImmutablePropertyUtils.implementsCloneable; import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching; import static org.codehaus.groovy.ast.tools.GeneralUtils.args; import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS; @@ -74,33 +66,24 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.block; import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX; import static org.codehaus.groovy.ast.tools.GeneralUtils.callX; import static org.codehaus.groovy.ast.tools.GeneralUtils.castX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.classList2args; import static org.codehaus.groovy.ast.tools.GeneralUtils.constX; import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX; import static org.codehaus.groovy.ast.tools.GeneralUtils.declS; import static org.codehaus.groovy.ast.tools.GeneralUtils.eqX; import static org.codehaus.groovy.ast.tools.GeneralUtils.equalsNullX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.findArg; import static org.codehaus.groovy.ast.tools.GeneralUtils.getGetterName; import static org.codehaus.groovy.ast.tools.GeneralUtils.getInstanceProperties; import static org.codehaus.groovy.ast.tools.GeneralUtils.hasDeclaredMethod; import static org.codehaus.groovy.ast.tools.GeneralUtils.ifElseS; import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS; -import static org.codehaus.groovy.ast.tools.GeneralUtils.isInstanceOfX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.isOneX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.isOrImplements; import static org.codehaus.groovy.ast.tools.GeneralUtils.isTrueX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.list2args; import static org.codehaus.groovy.ast.tools.GeneralUtils.neX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.notX; import static org.codehaus.groovy.ast.tools.GeneralUtils.orX; import static org.codehaus.groovy.ast.tools.GeneralUtils.params; -import static org.codehaus.groovy.ast.tools.GeneralUtils.propX; import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS; import static org.codehaus.groovy.ast.tools.GeneralUtils.safeExpression; import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt; import static org.codehaus.groovy.ast.tools.GeneralUtils.ternaryX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS; import static org.codehaus.groovy.ast.tools.GeneralUtils.varX; /** @@ -108,88 +91,14 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.varX; */ @GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION) public class ImmutableASTTransformation extends AbstractASTTransformation { - - /* - Currently leaving BigInteger and BigDecimal in list but see: - http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6348370 - - Also, Color is not final so while not normally used with child - classes, it isn't strictly immutable. Use at your own risk. - - This list can by extended by providing "known immutable" classes - via Immutable.knownImmutableClasses - */ - private static Set<String> builtinImmutables = new HashSet(Arrays.asList( - "java.lang.Class", - "java.lang.Boolean", - "java.lang.Byte", - "java.lang.Character", - "java.lang.Double", - "java.lang.Float", - "java.lang.Integer", - "java.lang.Long", - "java.lang.Short", - "java.lang.String", - "java.math.BigInteger", - "java.math.BigDecimal", - "java.awt.Color", - "java.net.URI", - "java.util.UUID", - "java.time.DayOfWeek", - "java.time.Duration", - "java.time.Instant", - "java.time.LocalDate", - "java.time.LocalDateTime", - "java.time.LocalTime", - "java.time.Month", - "java.time.MonthDay", - "java.time.OffsetDateTime", - "java.time.OffsetTime", - "java.time.Period", - "java.time.Year", - "java.time.YearMonth", - "java.time.ZonedDateTime", - "java.time.ZoneOffset", - "java.time.ZoneRegion", - "java.time.chrono.ChronoLocalDate", - "java.time.chrono.ChronoLocalDateTime", - "java.time.chrono.Chronology", - "java.time.chrono.ChronoPeriod", - "java.time.chrono.ChronoZonedDateTime", - "java.time.chrono.Era", - "java.time.format.DecimalStyle", - "java.time.format.FormatStyle", - "java.time.format.ResolverStyle", - "java.time.format.SignStyle", - "java.time.format.TextStyle", - "java.time.temporal.IsoFields", - "java.time.temporal.JulianFields", - "java.time.temporal.ValueRange", - "java.time.temporal.WeekFields" - )); - private static final String KNOWN_IMMUTABLE_NAME = KnownImmutable.class.getName(); private static final Class<? extends Annotation> MY_CLASS = ImmutableBase.class; - private static final ClassNode IMMUTABLE_BASE_TYPE = makeWithoutCaching(MY_CLASS, false); - public static final ClassNode MY_TYPE = make(MY_CLASS); - static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage(); - private static final String MEMBER_KNOWN_IMMUTABLE_CLASSES = "knownImmutableClasses"; - private static final String MEMBER_KNOWN_IMMUTABLES = "knownImmutables"; + public static final ClassNode MY_TYPE = makeWithoutCaching(MY_CLASS, false); + private static final String MY_TYPE_NAME = MY_TYPE.getNameWithoutPackage(); + private static final String MEMBER_ADD_COPY_WITH = "copyWith"; private static final String COPY_WITH_METHOD = "copyWith"; - private static final ClassNode DATE_TYPE = make(Date.class); - private static final ClassNode CLONEABLE_TYPE = make(Cloneable.class); - private static final ClassNode COLLECTION_TYPE = makeWithoutCaching(Collection.class, false); - private static final ClassNode READONLYEXCEPTION_TYPE = make(ReadOnlyPropertyException.class); - private static final ClassNode DGM_TYPE = make(DefaultGroovyMethods.class); - private static final ClassNode SELF_TYPE = make(ImmutableASTTransformation.class); private static final ClassNode HMAP_TYPE = makeWithoutCaching(HashMap.class, false); - private static final ClassNode MAP_TYPE = makeWithoutCaching(Map.class, false); - private static final ClassNode REFLECTION_INVOKER_TYPE = make(ReflectionMethodInvoker.class); - private static final ClassNode SORTEDSET_CLASSNODE = make(SortedSet.class); - private static final ClassNode SORTEDMAP_CLASSNODE = make(SortedMap.class); - private static final ClassNode SET_CLASSNODE = make(Set.class); - private static final ClassNode MAP_CLASSNODE = make(Map.class); public static final String IMMUTABLE_SAFE_FLAG = "Immutable.Safe"; public void visit(ASTNode[] nodes, SourceUnit source) { @@ -233,7 +142,7 @@ public class ImmutableASTTransformation extends AbstractASTTransformation { if (unsupportedTupleAttribute(tupleCons, "includeProperties")) return; if (unsupportedTupleAttribute(tupleCons, "includeSuperFields")) return; if (unsupportedTupleAttribute(tupleCons, "callSuper")) return; - if (unsupportedTupleAttribute(tupleCons, "useSetters")) return; +// if (unsupportedTupleAttribute(tupleCons, "useSetters")) return; if (unsupportedTupleAttribute(tupleCons, "force")) return; } if (!validateConstructors(cNode)) return; @@ -253,7 +162,7 @@ public class ImmutableASTTransformation extends AbstractASTTransformation { return false; } - private static void doAddConstructor(final ClassNode cNode, final ConstructorNode constructorNode) { + static void doAddConstructor(final ClassNode cNode, final ConstructorNode constructorNode) { cNode.addConstructor(constructorNode); // GROOVY-5814: Immutable is not compatible with @CompileStatic Parameter argsParam = null; @@ -283,50 +192,6 @@ public class ImmutableASTTransformation extends AbstractASTTransformation { } } - private static List<String> getKnownImmutableClasses(AbstractASTTransformation xform, AnnotationNode node) { - final List<String> immutableClasses = new ArrayList<String>(); - - if (node == null) return immutableClasses; - final Expression expression = node.getMember(MEMBER_KNOWN_IMMUTABLE_CLASSES); - if (expression == null) return immutableClasses; - - if (!(expression instanceof ListExpression)) { - xform.addError("Use the Groovy list notation [el1, el2] to specify known immutable classes via \"" + MEMBER_KNOWN_IMMUTABLE_CLASSES + "\"", node); - return immutableClasses; - } - - final ListExpression listExpression = (ListExpression) expression; - for (Expression listItemExpression : listExpression.getExpressions()) { - if (listItemExpression instanceof ClassExpression) { - immutableClasses.add(listItemExpression.getType().getName()); - } - } - - return immutableClasses; - } - - private static List<String> getKnownImmutables(AbstractASTTransformation xform, AnnotationNode node) { - final List<String> immutables = new ArrayList<String>(); - - if (node == null) return immutables; - final Expression expression = node.getMember(MEMBER_KNOWN_IMMUTABLES); - if (expression == null) return immutables; - - if (!(expression instanceof ListExpression)) { - xform.addError("Use the Groovy list notation [el1, el2] to specify known immutable property names via \"" + MEMBER_KNOWN_IMMUTABLES + "\"", node); - return immutables; - } - - final ListExpression listExpression = (ListExpression) expression; - for (Expression listItemExpression : listExpression.getExpressions()) { - if (listItemExpression instanceof ConstantExpression) { - immutables.add((String) ((ConstantExpression) listItemExpression).getValue()); - } - } - - return immutables; - } - private static void makeClassFinal(AbstractASTTransformation xform, ClassNode cNode) { int modifiers = cNode.getModifiers(); if ((modifiers & ACC_FINAL) == 0) { @@ -390,90 +255,6 @@ public class ImmutableASTTransformation extends AbstractASTTransformation { return stmt(fieldExpr); } - private static Expression cloneCollectionExpr(Expression fieldExpr, ClassNode type) { - return castX(type, createIfInstanceOfAsImmutableS(fieldExpr, SORTEDSET_CLASSNODE, - createIfInstanceOfAsImmutableS(fieldExpr, SORTEDMAP_CLASSNODE, - createIfInstanceOfAsImmutableS(fieldExpr, SET_CLASSNODE, - createIfInstanceOfAsImmutableS(fieldExpr, MAP_CLASSNODE, - createIfInstanceOfAsImmutableS(fieldExpr, ClassHelper.LIST_TYPE, - createAsImmutableX(fieldExpr, COLLECTION_TYPE)) - ) - ) - ) - )); - } - - private static Expression createIfInstanceOfAsImmutableS(Expression expr, ClassNode type, Expression elseStatement) { - return ternaryX(isInstanceOfX(expr, type), createAsImmutableX(expr, type), elseStatement); - } - - private static Expression createAsImmutableX(final Expression expr, final ClassNode type) { - return callX(DGM_TYPE, "asImmutable", castX(type, expr)); - } - - private static Expression cloneArrayOrCloneableExpr(Expression fieldExpr, ClassNode type) { - Expression smce = callX( - REFLECTION_INVOKER_TYPE, - "invoke", - args( - fieldExpr, - constX("clone"), - new ArrayExpression(ClassHelper.OBJECT_TYPE.makeArray(), Collections.<Expression>emptyList()) - ) - ); - return castX(type, smce); - } - - static void createConstructorMapCommon(ClassNode cNode, BlockStatement body, Parameter[] params) { - final List<FieldNode> fList = cNode.getFields(); - for (FieldNode fNode : fList) { - if (fNode.isPublic()) continue; // public fields will be rejected elsewhere - if (cNode.getProperty(fNode.getName()) != null) continue; // a property - if (fNode.isFinal() && fNode.isStatic()) continue; - if (fNode.getName().contains("$") || fNode.isSynthetic()) continue; // internal field - if (fNode.isFinal() && fNode.getInitialExpression() != null) - body.addStatement(checkFinalArgNotOverridden(cNode, fNode)); - body.addStatement(createConstructorStatementDefault(fNode, true)); - } - doAddConstructor(cNode, new ConstructorNode(ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, body)); - } - - private static Statement checkFinalArgNotOverridden(ClassNode cNode, FieldNode fNode) { - final String name = fNode.getName(); - Expression value = findArg(name); - return ifS( - notX(equalsNullX(value)), - throwS(ctorX(READONLYEXCEPTION_TYPE, - args(constX(name), constX(cNode.getName())) - ))); - } - - static Statement createConstructorStatementMapSpecial(FieldNode fNode) { - final Expression fieldExpr = varX(fNode); - final ClassNode fieldType = fieldExpr.getType(); - final Expression initExpr = fNode.getInitialValueExpression(); - final Statement assignInit; - if (initExpr == null || (initExpr instanceof ConstantExpression && ((ConstantExpression) initExpr).isNullExpression())) { - assignInit = assignS(fieldExpr, ConstantExpression.EMPTY_EXPRESSION); - } else { - assignInit = assignS(fieldExpr, cloneCollectionExpr(initExpr, fieldType)); - } - Expression namedArgs = findArg(fNode.getName()); - Expression baseArgs = varX("args"); - Statement assignStmt = ifElseS( - equalsNullX(namedArgs), - ifElseS( - isTrueX(callX(baseArgs, "containsKey", constX(fNode.getName()))), - assignS(fieldExpr, namedArgs), - assignS(fieldExpr, cloneCollectionExpr(baseArgs, fieldType))), - ifElseS( - isOneX(callX(baseArgs, "size")), - assignS(fieldExpr, cloneCollectionExpr(namedArgs, fieldType)), - assignS(fieldExpr, cloneCollectionExpr(baseArgs, fieldType))) - ); - return ifElseS(equalsNullX(baseArgs), assignInit, assignStmt); - } - private static void ensureNotPublic(AbstractASTTransformation xform, String cNode, FieldNode fNode) { String fName = fNode.getName(); // TODO: do we need to lock down things like: $ownClass @@ -507,137 +288,11 @@ public class ImmutableASTTransformation extends AbstractASTTransformation { } static boolean makeImmutable(ClassNode cNode) { - List<AnnotationNode> annotations = cNode.getAnnotations(IMMUTABLE_BASE_TYPE); + List<AnnotationNode> annotations = cNode.getAnnotations(ImmutablePropertyUtils.IMMUTABLE_BASE_TYPE); AnnotationNode annoImmutable = annotations.isEmpty() ? null : annotations.get(0); return annoImmutable != null; } - static Statement createConstructorStatement(AbstractASTTransformation xform, ClassNode cNode, PropertyNode pNode, boolean namedArgs) { - List<AnnotationNode> annotations = cNode.getAnnotations(IMMUTABLE_BASE_TYPE); - AnnotationNode annoImmutable = annotations.isEmpty() ? null : annotations.get(0); - final List<String> knownImmutableClasses = getKnownImmutableClasses(xform, annoImmutable); - final List<String> knownImmutables = getKnownImmutables(xform, annoImmutable); - FieldNode fNode = pNode.getField(); - final ClassNode fieldType = fNode.getType(); - Statement statement; - if (isKnownImmutableType(fieldType, knownImmutableClasses) || isKnownImmutable(pNode.getName(), knownImmutables)) { - statement = createConstructorStatementDefault(fNode, namedArgs); - } else if (fieldType.isArray() || isOrImplements(fieldType, CLONEABLE_TYPE)) { - statement = createConstructorStatementArrayOrCloneable(fNode, namedArgs); - } else if (fieldType.isDerivedFrom(DATE_TYPE)) { - statement = createConstructorStatementDate(fNode, namedArgs); - } else if (isOrImplements(fieldType, COLLECTION_TYPE) || fieldType.isDerivedFrom(COLLECTION_TYPE) || isOrImplements(fieldType, MAP_TYPE) || fieldType.isDerivedFrom(MAP_TYPE)) { - statement = createConstructorStatementCollection(fNode, namedArgs); - } else if (fieldType.isResolved()) { - xform.addError(createErrorMessage(cNode.getName(), fNode.getName(), fieldType.getName(), "compiling"), fNode); - statement = EmptyStatement.INSTANCE; - } else { - statement = createConstructorStatementGuarded(cNode, fNode, namedArgs, knownImmutables, knownImmutableClasses); - } - return statement; - } - - private static Statement createConstructorStatementDefault(FieldNode fNode, boolean namedArgs) { - final ClassNode fType = fNode.getType(); - final Expression fieldExpr = propX(varX("this"), fNode.getName()); - Expression initExpr = fNode.getInitialValueExpression(); - Statement assignInit; - if (initExpr == null || (initExpr instanceof ConstantExpression && ((ConstantExpression)initExpr).isNullExpression())) { - if (ClassHelper.isPrimitiveType(fType)) { - assignInit = EmptyStatement.INSTANCE; - } else { - assignInit = assignS(fieldExpr, ConstantExpression.EMPTY_EXPRESSION); - } - } else { - assignInit = assignS(fieldExpr, initExpr); - } - fNode.setInitialValueExpression(null); - Expression param = getParam(fNode, namedArgs); - Statement assignStmt = assignS(fieldExpr, castX(fType, param)); - return assignWithDefault(namedArgs, assignInit, param, assignStmt); - } - - private static Statement assignWithDefault(boolean namedArgs, Statement assignInit, Expression param, Statement assignStmt) { - if (!namedArgs) { - return assignStmt; - } - return ifElseS(equalsNullX(param), assignInit, assignStmt); - } - - private static Statement createConstructorStatementGuarded(ClassNode cNode, FieldNode fNode, boolean namedArgs, List<String> knownImmutables, List<String> knownImmutableClasses) { - final Expression fieldExpr = propX(varX("this"), fNode.getName()); - Expression initExpr = fNode.getInitialValueExpression(); - final Statement assignInit; - if (initExpr == null || (initExpr instanceof ConstantExpression && ((ConstantExpression) initExpr).isNullExpression())) { - assignInit = assignS(fieldExpr, ConstantExpression.EMPTY_EXPRESSION); - } else { - assignInit = assignS(fieldExpr, checkUnresolved(fNode, initExpr, knownImmutables, knownImmutableClasses)); - } - Expression param = getParam(fNode, namedArgs); - Statement assignStmt = assignS(fieldExpr, checkUnresolved(fNode, param, knownImmutables, knownImmutableClasses)); - return assignWithDefault(namedArgs, assignInit, param, assignStmt); - } - - private static Expression checkUnresolved(FieldNode fNode, Expression value, List<String> knownImmutables, List<String> knownImmutableClasses) { - Expression args = args(callThisX("getClass"), constX(fNode.getName()), value, list2args(knownImmutables), classList2args(knownImmutableClasses)); - return callX(SELF_TYPE, "checkImmutable", args); - } - - private static Statement createConstructorStatementCollection(FieldNode fNode, boolean namedArgs) { - final Expression fieldExpr = propX(varX("this"), fNode.getName()); - ClassNode fieldType = fieldExpr.getType(); - Expression initExpr = fNode.getInitialValueExpression(); - final Statement assignInit; - if (initExpr == null || (initExpr instanceof ConstantExpression && ((ConstantExpression) initExpr).isNullExpression())) { - assignInit = assignS(fieldExpr, ConstantExpression.EMPTY_EXPRESSION); - } else { - assignInit = assignS(fieldExpr, cloneCollectionExpr(initExpr, fieldType)); - } - Expression param = getParam(fNode, namedArgs); - Statement assignStmt = ifElseS( - isInstanceOfX(param, CLONEABLE_TYPE), - assignS(fieldExpr, cloneCollectionExpr(cloneArrayOrCloneableExpr(param, fieldType), fieldType)), - assignS(fieldExpr, cloneCollectionExpr(param, fieldType))); - return assignWithDefault(namedArgs, assignInit, param, assignStmt); - } - - private static Statement createConstructorStatementArrayOrCloneable(FieldNode fNode, boolean namedArgs) { - final Expression fieldExpr = propX(varX("this"), fNode.getName()); - final Expression initExpr = fNode.getInitialValueExpression(); - final ClassNode fieldType = fNode.getType(); - final Expression param = getParam(fNode, namedArgs); - final Statement assignInit; - if (initExpr == null || (initExpr instanceof ConstantExpression && ((ConstantExpression) initExpr).isNullExpression())) { - assignInit = assignS(fieldExpr, ConstantExpression.EMPTY_EXPRESSION); - } else { - assignInit = assignS(fieldExpr, cloneArrayOrCloneableExpr(initExpr, fieldType)); - } - Statement assignStmt = assignS(fieldExpr, cloneArrayOrCloneableExpr(param, fieldType)); - return assignWithDefault(namedArgs, assignInit, param, assignStmt); - } - - private static Expression getParam(FieldNode fNode, boolean namedArgs) { - return namedArgs ? findArg(fNode.getName()) : varX(fNode.getName(), fNode.getType()); - } - - private static Statement createConstructorStatementDate(FieldNode fNode, boolean namedArgs) { - final Expression fieldExpr = propX(varX("this"), fNode.getName()); - Expression initExpr = fNode.getInitialValueExpression(); - final Statement assignInit; - if (initExpr == null || (initExpr instanceof ConstantExpression && ((ConstantExpression) initExpr).isNullExpression())) { - assignInit = assignS(fieldExpr, ConstantExpression.EMPTY_EXPRESSION); - } else { - assignInit = assignS(fieldExpr, cloneDateExpr(initExpr)); - } - final Expression param = getParam(fNode, namedArgs); - Statement assignStmt = assignS(fieldExpr, cloneDateExpr(param)); - return assignWithDefault(namedArgs, assignInit, param, assignStmt); - } - - private static Expression cloneDateExpr(Expression origDate) { - return ctorX(DATE_TYPE, callX(origDate, "getTime")); - } - private static void adjustPropertyForImmutability(PropertyNode pNode, List<PropertyNode> newNodes) { final FieldNode fNode = pNode.getField(); fNode.setModifiers((pNode.getModifiers() & (~ACC_PUBLIC)) | ACC_FINAL | ACC_PRIVATE); @@ -654,9 +309,9 @@ public class ImmutableASTTransformation extends AbstractASTTransformation { BlockStatement body = new BlockStatement(); final ClassNode fieldType = fNode.getType(); final Statement statement; - if (fieldType.isArray() || isOrImplements(fieldType, CLONEABLE_TYPE)) { + if (fieldType.isArray() || implementsCloneable(fieldType)) { statement = createGetterBodyArrayOrCloneable(fNode); - } else if (fieldType.isDerivedFrom(DATE_TYPE)) { + } else if (derivesFromDate(fieldType)) { statement = createGetterBodyDate(fNode); } else { statement = createGetterBodyDefault(fNode); @@ -665,20 +320,6 @@ public class ImmutableASTTransformation extends AbstractASTTransformation { return body; } - private static String createErrorMessage(String className, String fieldName, String typeName, String mode) { - return "Unsupported type (" + prettyTypeName(typeName) + ") found for field '" + fieldName + "' while " + mode + " immutable class " + className + ".\n" + - "Immutable classes only support properties with effectively immutable types including:\n" + - "- Strings, primitive types, wrapper types, Class, BigInteger and BigDecimal, enums\n" + - "- classes annotated with @KnownImmutable and known immutables (java.awt.Color, java.net.URI)\n" + - "- Cloneable classes, collections, maps and arrays, and other classes with special handling\n" + - " (java.util.Date and various java.time.* classes and interfaces)\n" + - "Other restrictions apply, please see the groovydoc for " + MY_TYPE_NAME + " for further details"; - } - - private static String prettyTypeName(String name) { - return name.equals("java.lang.Object") ? name + " or def" : name; - } - private static Statement createGetterBodyArrayOrCloneable(FieldNode fNode) { final Expression fieldExpr = varX(fNode); final Expression expression = cloneArrayOrCloneableExpr(fieldExpr, fNode.getType()); @@ -791,7 +432,7 @@ public class ImmutableASTTransformation extends AbstractASTTransformation { */ @SuppressWarnings("Unchecked") public static Object checkImmutable(String className, String fieldName, Object field) { - if (field == null || field instanceof Enum || isBuiltinImmutable(field.getClass().getName())) return field; + if (field == null || field instanceof Enum || ImmutablePropertyUtils.isBuiltinImmutable(field.getClass().getName())) return field; if (field instanceof Collection) return DefaultGroovyMethods.asImmutable((Collection) field); if (getAnnotationByName(field, "groovy.transform.Immutable") != null) return field; @@ -882,62 +523,6 @@ public class ImmutableASTTransformation extends AbstractASTTransformation { throw new RuntimeException(createErrorMessage(clazz.getName(), fieldName, typeName, "constructing")); } - private static boolean isKnownImmutableType(ClassNode fieldType, List<String> knownImmutableClasses) { - if (builtinOrDeemedType(fieldType, knownImmutableClasses)) - return true; - if (!fieldType.isResolved()) - return false; - if ("java.util.Optional".equals(fieldType.getName()) && fieldType.getGenericsTypes() != null && fieldType.getGenericsTypes().length == 1) { - GenericsType optionalType = fieldType.getGenericsTypes()[0]; - if (optionalType.isResolved() && !optionalType.isPlaceholder() && !optionalType.isWildcard()) { - ClassNode valueType = optionalType.getType(); - if (builtinOrDeemedType(valueType, knownImmutableClasses)) return true; - if (valueType.isEnum()) return true; - } - } - return fieldType.isEnum() || - ClassHelper.isPrimitiveType(fieldType) || - hasImmutableAnnotation(fieldType); - } - - private static boolean builtinOrDeemedType(ClassNode fieldType, List<String> knownImmutableClasses) { - return isBuiltinImmutable(fieldType.getName()) || knownImmutableClasses.contains(fieldType.getName()) || hasImmutableAnnotation(fieldType); - } - - private static boolean hasImmutableAnnotation(ClassNode type) { - List<AnnotationNode> annotations = type.getAnnotations(); - for (AnnotationNode next : annotations) { - String name = next.getClassNode().getName(); - if (matchingMarkerName(name)) return true; - } - return false; - } - - private static boolean hasImmutableAnnotation(Class clazz) { - Annotation[] annotations = clazz.getAnnotations(); - for (Annotation next : annotations) { - String name = next.annotationType().getName(); - if (matchingMarkerName(name)) return true; - } - return false; - } - - private static boolean matchingMarkerName(String name) { - return name.equals("groovy.transform.Immutable") || name.equals(KNOWN_IMMUTABLE_NAME); - } - - private static boolean isKnownImmutable(String fieldName, List<String> knownImmutables) { - return knownImmutables.contains(fieldName); - } - - private static boolean isBuiltinImmutable(String typeName) { - return builtinImmutables.contains(typeName); - } - - private static boolean builtinOrMarkedImmutableClass(Class<?> clazz) { - return isBuiltinImmutable(clazz.getName()) || hasImmutableAnnotation(clazz); - } - public static void checkPropNames(Object instance, Map<String, Object> args) { final MetaClass metaClass = InvokerHelper.getMetaClass(instance); for (String k : args.keySet()) { http://git-wip-us.apache.org/repos/asf/groovy/blob/69fc7ab3/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java index bb6becb..3859f72 100644 --- a/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java +++ b/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java @@ -18,7 +18,10 @@ */ package org.codehaus.groovy.transform; +import groovy.lang.GroovyClassLoader; +import groovy.transform.CompilationUnitAware; import groovy.transform.MapConstructor; +import groovy.transform.construction.PropertyHandler; import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.AnnotatedNode; import org.codehaus.groovy.ast.AnnotationNode; @@ -28,7 +31,6 @@ import org.codehaus.groovy.ast.ConstructorNode; import org.codehaus.groovy.ast.DynamicVariable; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.PropertyNode; -import org.codehaus.groovy.ast.expr.ArgumentListExpression; import org.codehaus.groovy.ast.expr.ClosureExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.MapExpression; @@ -36,6 +38,7 @@ import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.ast.stmt.BlockStatement; import org.codehaus.groovy.ast.stmt.EmptyStatement; import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.control.CompilationUnit; import org.codehaus.groovy.control.CompilePhase; import org.codehaus.groovy.control.SourceUnit; @@ -50,38 +53,33 @@ import static org.apache.groovy.ast.tools.ClassNodeUtils.hasNoArgConstructor; import static org.codehaus.groovy.ast.ClassHelper.make; import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching; import static org.codehaus.groovy.ast.tools.GeneralUtils.args; -import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS; -import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.callX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.constX; import static org.codehaus.groovy.ast.tools.GeneralUtils.copyStatementsWithSuperAdjustment; import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.equalsNullX; import static org.codehaus.groovy.ast.tools.GeneralUtils.getAllProperties; -import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName; -import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS; import static org.codehaus.groovy.ast.tools.GeneralUtils.param; import static org.codehaus.groovy.ast.tools.GeneralUtils.params; -import static org.codehaus.groovy.ast.tools.GeneralUtils.propX; import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt; -import static org.codehaus.groovy.ast.tools.GeneralUtils.varX; -import static org.codehaus.groovy.transform.ImmutableASTTransformation.createConstructorMapCommon; -import static org.codehaus.groovy.transform.ImmutableASTTransformation.createConstructorStatement; -import static org.codehaus.groovy.transform.ImmutableASTTransformation.createConstructorStatementMapSpecial; +import static org.codehaus.groovy.transform.ImmutableASTTransformation.doAddConstructor; /** * Handles generation of code for the @MapConstructor annotation. */ @GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION) -public class MapConstructorASTTransformation extends AbstractASTTransformation { +public class MapConstructorASTTransformation extends AbstractASTTransformation implements CompilationUnitAware { + + private CompilationUnit compilationUnit; static final Class MY_CLASS = MapConstructor.class; static final ClassNode MY_TYPE = make(MY_CLASS); static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage(); private static final ClassNode MAP_TYPE = makeWithoutCaching(Map.class, false); - private static final ClassNode IMMUTABLE_XFORM_TYPE = make(ImmutableASTTransformation.class); private static final ClassNode LHMAP_TYPE = makeWithoutCaching(LinkedHashMap.class, false); + @Override + public String getAnnotationName() { + return MY_TYPE_NAME; + } + public void visit(ASTNode[] nodes, SourceUnit source) { init(nodes, source); AnnotatedNode parent = (AnnotatedNode) nodes[1]; @@ -95,9 +93,11 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation { boolean includeProperties = !memberHasValue(anno, "includeProperties", false); boolean includeSuperProperties = memberHasValue(anno, "includeSuperProperties", true); boolean includeSuperFields = memberHasValue(anno, "includeSuperFields", true); - boolean useSetters = memberHasValue(anno, "useSetters", true); +// boolean useSetters = memberHasValue(anno, "useSetters", true); + boolean includeStatic = memberHasValue(anno, "includeStatic", true); boolean allProperties = memberHasValue(anno, "allProperties", true); boolean noArg = memberHasValue(anno, "noArg", true); + boolean specialNamedArgHandling = !memberHasValue(anno, "specialNamedArgHandling", false); List<String> excludes = getMemberStringList(anno, "excludes"); List<String> includes = getMemberStringList(anno, "includes"); boolean allNames = memberHasValue(anno, "allNames", true); @@ -106,6 +106,10 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation { return; if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields, includeSuperProperties, false)) return; + final GroovyClassLoader classLoader = compilationUnit != null ? compilationUnit.getTransformLoader() : source.getClassLoader(); + final PropertyHandler handler = PropertyHandler.createPropertyHandler(this, anno, classLoader); + if (handler == null) return; + if (!handler.validateAttributes(this, anno)) return; Expression pre = anno.getMember("pre"); if (pre != null && !(pre instanceof ClosureExpression)) { @@ -118,7 +122,7 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation { return; } - createConstructors(this, cNode, includeFields, includeProperties, includeSuperProperties, includeSuperFields, useSetters, noArg, allNames, allProperties, excludes, includes, (ClosureExpression) pre, (ClosureExpression) post, source); + createConstructors(this, anno, handler, cNode, includeFields, includeProperties, includeSuperProperties, includeSuperFields, noArg, allNames, allProperties, specialNamedArgHandling, includeStatic, excludes, includes, (ClosureExpression) pre, (ClosureExpression) post, source); if (pre != null) { anno.setMember("pre", new ClosureExpression(new Parameter[0], EmptyStatement.INSTANCE)); @@ -129,7 +133,10 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation { } } - private static void createConstructors(AbstractASTTransformation xform, ClassNode cNode, boolean includeFields, boolean includeProperties, boolean includeSuperProperties, boolean includeSuperFields, boolean useSetters, boolean noArg, boolean allNames, boolean allProperties, List<String> excludes, List<String> includes, ClosureExpression pre, ClosureExpression post, SourceUnit source) { + private static void createConstructors(AbstractASTTransformation xform, AnnotationNode anno, PropertyHandler handler, ClassNode cNode, boolean includeFields, boolean includeProperties, + boolean includeSuperProperties, boolean includeSuperFields, boolean noArg, + boolean allNames, boolean allProperties, boolean specialNamedArgHandling, boolean includeStatic, + List<String> excludes, List<String> includes, ClosureExpression pre, ClosureExpression post, SourceUnit source) { List<ConstructorNode> constructors = cNode.getDeclaredConstructors(); boolean foundEmpty = constructors.size() == 1 && constructors.get(0).getFirstStatement() == null; // HACK: JavaStubGenerator could have snuck in a constructor we don't want @@ -138,13 +145,11 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation { Set<String> names = new HashSet<String>(); List<PropertyNode> superList; if (includeSuperProperties || includeSuperFields) { - superList = getAllProperties(names, cNode, cNode.getSuperClass(), includeSuperProperties, includeSuperFields, false, allProperties, true, true); + superList = getAllProperties(names, cNode, cNode.getSuperClass(), includeSuperProperties, includeSuperFields, false, allProperties, true, false, false, allNames, includeStatic); } else { superList = new ArrayList<PropertyNode>(); } - List<PropertyNode> list = getAllProperties(names, cNode, true, includeFields, false, allProperties, false, true); - - boolean makeImmutable = ImmutableASTTransformation.makeImmutable(cNode); + List<PropertyNode> list = getAllProperties(names, cNode, cNode, includeProperties, includeFields, false, allProperties, false, false, false, allNames, includeStatic); Parameter map = param(MAP_TYPE, "args"); final BlockStatement body = new BlockStatement(); @@ -155,39 +160,30 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation { } final BlockStatement inner = new BlockStatement(); superList.addAll(list); - boolean specialNamedArgCase = ImmutableASTTransformation.isSpecialNamedArgCase(superList, true); - if (!specialNamedArgCase) { - processProps(xform, cNode, makeImmutable, useSetters, allNames, excludes, includes, superList, map, inner); - body.addStatement(ifS(equalsNullX(varX("args")), assignS(varX("args"), new MapExpression()))); + + if (!handler.validateProperties(xform, body, cNode, superList)) { + return; } + + boolean specialNamedArgCase = specialNamedArgHandling && ImmutableASTTransformation.isSpecialNamedArgCase(superList, true); + processProps(xform, anno, cNode, handler, allNames, excludes, includes, superList, map, inner); body.addStatement(inner); + Parameter[] params = params(specialNamedArgCase ? new Parameter(LHMAP_TYPE, "args") : map); if (post != null) { ClosureExpression transformed = (ClosureExpression) transformer.transform(post); body.addStatement(transformed.getCode()); } - if (makeImmutable && !specialNamedArgCase) { - body.addStatement(stmt(callX(IMMUTABLE_XFORM_TYPE, "checkPropNames", args("this", "args")))); - createConstructorMapCommon(cNode, body, params(map)); - } else if (specialNamedArgCase) { - inner.addStatement(createConstructorStatementMapSpecial(superList.get(0).getField())); - createConstructorMapCommon(cNode, body, params(new Parameter(LHMAP_TYPE, "args"))); - } else { - cNode.addConstructor(new ConstructorNode(ACC_PUBLIC, params(map), ClassNode.EMPTY_ARRAY, body)); - } - if (noArg && !superList.isEmpty() && !hasNoArgConstructor(cNode) && !specialNamedArgCase) { + doAddConstructor(cNode, new ConstructorNode(ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, body)); + if (noArg && !superList.isEmpty() && !hasNoArgConstructor(cNode)/* && !specialNamedArgCase*/) { createNoArgConstructor(cNode); } } - private static void processProps(AbstractASTTransformation xform, ClassNode cNode, boolean makeImmutable, boolean useSetters, boolean allNames, List<String> excludes, List<String> includes, List<PropertyNode> superList, Parameter map, BlockStatement inner) { + private static void processProps(AbstractASTTransformation xform, AnnotationNode anno, ClassNode cNode, PropertyHandler handler, boolean allNames, List<String> excludes, List<String> includes, List<PropertyNode> superList, Parameter map, BlockStatement inner) { for (PropertyNode pNode : superList) { String name = pNode.getName(); if (shouldSkipUndefinedAware(name, excludes, includes, allNames)) continue; - if (makeImmutable) { - inner.addStatement(createConstructorStatement(xform, cNode, pNode, true)); - } else { - assignField(useSetters, map, inner, name); - } + handler.createStatement(xform, anno, inner, cNode, pNode, map); } } @@ -196,13 +192,6 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation { cNode.addConstructor(new ConstructorNode(ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body)); } - private static void assignField(boolean useSetters, Parameter map, BlockStatement body, String name) { - ArgumentListExpression nameArg = args(constX(name)); - body.addStatement(ifS(callX(varX(map), "containsKey", nameArg), useSetters ? - stmt(callThisX(getSetterName(name), callX(varX(map), "get", nameArg))) : - assignS(propX(varX("this"), name), callX(varX(map), "get", nameArg)))); - } - private static ClassCodeExpressionTransformer makeMapTypedArgsTransformer() { return new ClassCodeExpressionTransformer() { @Override @@ -228,4 +217,8 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation { }; } + @Override + public void setCompilationUnit(CompilationUnit unit) { + this.compilationUnit = unit; + } } http://git-wip-us.apache.org/repos/asf/groovy/blob/69fc7ab3/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java index fc7daa9..018079d 100644 --- a/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java +++ b/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java @@ -192,11 +192,11 @@ public class ToStringASTTransformation extends AbstractASTTransformation { Set<String> names = new HashSet<String>(); List<PropertyNode> superList; if (includeSuperProperties || includeSuperFields) { - superList = getAllProperties(names, cNode, cNode.getSuperClass(), includeSuperProperties, includeSuperFields, allProperties, false, true, true, true); + superList = getAllProperties(names, cNode, cNode.getSuperClass(), includeSuperProperties, includeSuperFields, allProperties, false, true, true, true, allNames, false); } else { superList = new ArrayList<PropertyNode>(); } - List<PropertyNode> list = getAllProperties(names, cNode, true, includeFields, allProperties, false, false, true); + List<PropertyNode> list = getAllProperties(names, cNode, cNode,true, includeFields, allProperties, false, false, true, false, allNames, false); list.addAll(superList); for (PropertyNode pNode : list) { http://git-wip-us.apache.org/repos/asf/groovy/blob/69fc7ab3/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java index 278ad8f..13decb9 100644 --- a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java +++ b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java @@ -18,8 +18,11 @@ */ package org.codehaus.groovy.transform; +import groovy.lang.GroovyClassLoader; +import groovy.transform.CompilationUnitAware; import groovy.transform.MapConstructor; import groovy.transform.TupleConstructor; +import groovy.transform.construction.PropertyHandler; import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.AnnotatedNode; import org.codehaus.groovy.ast.AnnotationNode; @@ -37,6 +40,7 @@ import org.codehaus.groovy.ast.stmt.BlockStatement; import org.codehaus.groovy.ast.stmt.EmptyStatement; import org.codehaus.groovy.ast.stmt.Statement; import org.codehaus.groovy.classgen.VariableScopeVisitor; +import org.codehaus.groovy.control.CompilationUnit; import org.codehaus.groovy.control.CompilePhase; import org.codehaus.groovy.control.SourceUnit; @@ -71,15 +75,15 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.propX; import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt; import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS; import static org.codehaus.groovy.ast.tools.GeneralUtils.varX; -import static org.codehaus.groovy.transform.ImmutableASTTransformation.createConstructorStatement; import static org.codehaus.groovy.transform.ImmutableASTTransformation.makeImmutable; /** * Handles generation of code for the @TupleConstructor annotation. */ @GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION) -public class TupleConstructorASTTransformation extends AbstractASTTransformation { +public class TupleConstructorASTTransformation extends AbstractASTTransformation implements CompilationUnitAware { + private CompilationUnit compilationUnit; static final Class MY_CLASS = TupleConstructor.class; static final ClassNode MY_TYPE = make(MY_CLASS); static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage(); @@ -103,6 +107,11 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation primitivesInitialValues.put(boolean.class, ConstantExpression.FALSE); } + @Override + public String getAnnotationName() { + return MY_TYPE_NAME; + } + public void visit(ASTNode[] nodes, SourceUnit source) { init(nodes, source); AnnotatedNode parent = (AnnotatedNode) nodes[1]; @@ -116,17 +125,18 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation boolean includeProperties = !memberHasValue(anno, "includeProperties", false); boolean includeSuperFields = memberHasValue(anno, "includeSuperFields", true); boolean includeSuperProperties = memberHasValue(anno, "includeSuperProperties", true); - boolean callSuper = memberHasValue(anno, "callSuper", true); - boolean force = memberHasValue(anno, "force", true); - boolean defaults = !memberHasValue(anno, "defaults", false); - boolean useSetters = memberHasValue(anno, "useSetters", true); boolean allProperties = memberHasValue(anno, "allProperties", true); List<String> excludes = getMemberStringList(anno, "excludes"); List<String> includes = getMemberStringList(anno, "includes"); boolean allNames = memberHasValue(anno, "allNames", true); if (!checkIncludeExcludeUndefinedAware(anno, excludes, includes, MY_TYPE_NAME)) return; - if (!checkPropertyList(cNode, includes, "includes", anno, MY_TYPE_NAME, includeFields, includeSuperProperties, false, includeSuperFields)) return; - if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields, includeSuperProperties, false, includeSuperFields)) return; + if (!checkPropertyList(cNode, includes, "includes", anno, MY_TYPE_NAME, includeFields, includeSuperProperties, allProperties, includeSuperFields, false)) return; + if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields, includeSuperProperties, allProperties, includeSuperFields, false)) return; + final GroovyClassLoader classLoader = compilationUnit != null ? compilationUnit.getTransformLoader() : source.getClassLoader(); + final PropertyHandler handler = PropertyHandler.createPropertyHandler(this, anno, classLoader); + if (handler == null) return; + if (!handler.validateAttributes(this, anno)) return; + Expression pre = anno.getMember("pre"); if (pre != null && !(pre instanceof ClosureExpression)) { addError("Expected closure value for annotation parameter 'pre'. Found " + pre, cNode); @@ -138,9 +148,9 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation return; } - createConstructor(this, cNode, includeFields, includeProperties, includeSuperFields, includeSuperProperties, - callSuper, force, excludes, includes, useSetters, defaults, allNames, allProperties, - sourceUnit, (ClosureExpression) pre, (ClosureExpression) post); + createConstructor(this, anno, cNode, includeFields, includeProperties, includeSuperFields, includeSuperProperties, + excludes, includes, allNames, allProperties, + sourceUnit, handler, (ClosureExpression) pre, (ClosureExpression) post); if (pre != null) { anno.setMember("pre", new ClosureExpression(new Parameter[0], EmptyStatement.INSTANCE)); @@ -151,32 +161,14 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation } } - public static void createConstructor(ClassNode cNode, boolean includeFields, boolean includeProperties, boolean includeSuperFields, boolean includeSuperProperties, boolean callSuper, boolean force, List<String> excludes, List<String> includes, boolean useSetters) { - createConstructor(null, cNode, includeFields, includeProperties, includeSuperFields, includeSuperProperties, callSuper, force, excludes, includes, useSetters, true); - } - - public static void createConstructor(AbstractASTTransformation xform, ClassNode cNode, boolean includeFields, boolean includeProperties, boolean includeSuperFields, boolean includeSuperProperties, boolean callSuper, boolean force, List<String> excludes, List<String> includes, boolean useSetters, boolean defaults) { - createConstructor(xform, cNode, includeFields, includeProperties, includeSuperFields, includeSuperProperties, callSuper, force, excludes, includes, useSetters, defaults, false); - } - - public static void createConstructor(AbstractASTTransformation xform, ClassNode cNode, boolean includeFields, boolean includeProperties, boolean includeSuperFields, boolean includeSuperProperties, boolean callSuper, boolean force, List<String> excludes, List<String> includes, boolean useSetters, boolean defaults, boolean allNames) { - createConstructor(xform, cNode, includeFields, includeProperties, includeSuperFields, includeSuperProperties, - callSuper, force, excludes, includes, useSetters, defaults, false, null, null, null); - } - - public static void createConstructor(AbstractASTTransformation xform, ClassNode cNode, boolean includeFields, - boolean includeProperties, boolean includeSuperFields, boolean includeSuperProperties, - boolean callSuper, boolean force, List<String> excludes, final List<String> includes, - boolean useSetters, boolean defaults, boolean allNames, - SourceUnit sourceUnit, ClosureExpression pre, ClosureExpression post) { - createConstructor(xform, cNode, includeFields, includeProperties, includeSuperFields, includeSuperProperties, callSuper, force, excludes, includes, useSetters, defaults, allNames,false, sourceUnit, pre, post); - } - - public static void createConstructor(AbstractASTTransformation xform, ClassNode cNode, boolean includeFields, - boolean includeProperties, boolean includeSuperFields, boolean includeSuperProperties, - boolean callSuper, boolean force, List<String> excludes, final List<String> includes, - boolean useSetters, boolean defaults, boolean allNames, boolean allProperties, - SourceUnit sourceUnit, ClosureExpression pre, ClosureExpression post) { + private static void createConstructor(AbstractASTTransformation xform, AnnotationNode anno, ClassNode cNode, boolean includeFields, + boolean includeProperties, boolean includeSuperFields, boolean includeSuperProperties, + List<String> excludes, final List<String> includes, boolean allNames, boolean allProperties, + SourceUnit sourceUnit, PropertyHandler handler, ClosureExpression pre, ClosureExpression post) { + boolean callSuper = xform.memberHasValue(anno, "callSuper", true); + boolean force = xform.memberHasValue(anno, "force", true); + boolean defaults = !xform.memberHasValue(anno, "defaults", false); + boolean useSetters = xform.memberHasValue(anno, "useSetters", true); Set<String> names = new HashSet<String>(); List<PropertyNode> superList; if (includeSuperProperties || includeSuperFields) { @@ -205,7 +197,15 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation "closure and also 'callSuper' enabled", cNode); } } + final BlockStatement body = new BlockStatement(); + + List<PropertyNode> tempList = new ArrayList<PropertyNode>(list); + tempList.addAll(superList); + if (!handler.validateProperties(xform, body, cNode, tempList)) { + return; + } + for (PropertyNode pNode : superList) { String name = pNode.getName(); FieldNode fNode = pNode.getField(); @@ -215,15 +215,7 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation if (callSuper) { superParams.add(varX(name)); } else if (!superInPre && !specialNamedArgCase) { - if (makeImmutable) { - body.addStatement(createConstructorStatement(xform, cNode, pNode, false)); - } else { - if (useSetters && hasSetter) { - body.addStatement(stmt(callThisX(getSetterName(name), varX(name)))); - } else { - body.addStatement(assignS(propX(varX("this"), name), varX(name))); - } - } + handler.createStatement(xform, anno, body, cNode, pNode, null); } } if (callSuper) { @@ -239,16 +231,7 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation if (shouldSkipUndefinedAware(name, excludes, includes, allNames)) continue; Parameter nextParam = createParam(fNode, name, defaults, xform, makeImmutable); params.add(nextParam); - boolean hasSetter = cNode.getProperty(name) != null && !fNode.isFinal(); - if (makeImmutable) { - body.addStatement(createConstructorStatement(xform, cNode, pNode, false)); - } else { - if (useSetters && hasSetter) { - body.addStatement(stmt(callThisX(getSetterName(name), varX(nextParam)))); - } else { - body.addStatement(assignS(propX(varX("this"), name), varX(nextParam))); - } - } + handler.createStatement(xform, anno, body, cNode, pNode, null); } if (post != null) { @@ -265,9 +248,7 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation } boolean hasMapCons = hasAnnotation(cNode, MapConstructorASTTransformation.MY_TYPE); - if (!specialNamedArgCase || !hasMapCons) { - cNode.addConstructor(new ConstructorNode(ACC_PUBLIC, params.toArray(new Parameter[params.size()]), ClassNode.EMPTY_ARRAY, body)); - } + cNode.addConstructor(new ConstructorNode(ACC_PUBLIC, params.toArray(new Parameter[params.size()]), ClassNode.EMPTY_ARRAY, body)); if (sourceUnit != null && !body.isEmpty()) { VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(sourceUnit); @@ -346,4 +327,9 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation block.addStatement(stmt(callX(CHECK_METHOD_TYPE, "checkPropNames", args(varX("this"), namedArgs)))); return block; } + + @Override + public void setCompilationUnit(CompilationUnit unit) { + this.compilationUnit = unit; + } } http://git-wip-us.apache.org/repos/asf/groovy/blob/69fc7ab3/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy ---------------------------------------------------------------------- diff --git a/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy b/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy index e457855..2d817e5 100644 --- a/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy +++ b/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy @@ -230,7 +230,8 @@ class ImmutableTransformTest extends GroovyShellTestCase { void testImmutableWithHashMap() { assertScript """ import groovy.transform.Immutable - @Immutable final class HasHashMap { + @Immutable(propertyHandler = groovy.transform.construction.LegacyHashMapPropertyHandler, noArg = false) + final class HasHashMap { HashMap map = [d:4] } assert new HasHashMap([a:1]).map == [a:1] @@ -413,7 +414,8 @@ class ImmutableTransformTest extends GroovyShellTestCase { void testPrivateFieldAssignedViaConstructor() { assertScript ''' import groovy.transform.Immutable - @Immutable class Numbers { + @Immutable(includeStatic = true) + class Numbers { private int a1 = 1 private int b1 = -1 private int c1