Repository: groovy
Updated Branches:
  refs/heads/GROOVY_2_5_X ef4a7985d -> cb984a9ed


http://git-wip-us.apache.org/repos/asf/groovy/blob/479bfa50/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/479bfa50/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 1293d5b..8664bb8 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], 
new EmptyStatement()));
@@ -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/479bfa50/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/479bfa50/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 3ee15a6..2618fc9 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], 
new EmptyStatement()));
@@ -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/479bfa50/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

Reply via email to