Repository: groovy
Updated Branches:
  refs/heads/master 122dc3057 -> e2f4ceb50


http://git-wip-us.apache.org/repos/asf/groovy/blob/e2f4ceb5/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java 
b/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
index 5d286c7..cba687e 100644
--- a/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
@@ -144,8 +144,8 @@ public class EnumCompletionVisitor extends 
ClassCodeVisitorSupport {
     }
 
     private static void addMapConstructors(ClassNode enumClass) {
-        TupleConstructorASTTransformation.addSpecialMapConstructors(enumClass, 
true, "One of the enum constants for enum " + enumClass.getName() +
-                " was initialized with null. Please use a non-null value or 
define your own constructor.");
+        TupleConstructorASTTransformation.addSpecialMapConstructors(enumClass, 
"One of the enum constants for enum " + enumClass.getName() +
+                " was initialized with null. Please use a non-null value or 
define your own constructor.", true);
     }
 
     private String getUniqueVariableName(final String name, Statement code) {

http://git-wip-us.apache.org/repos/asf/groovy/blob/e2f4ceb5/src/main/java/org/codehaus/groovy/transform/AbstractASTTransformation.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/codehaus/groovy/transform/AbstractASTTransformation.java 
b/src/main/java/org/codehaus/groovy/transform/AbstractASTTransformation.java
index 23cadd6..8c1de6c 100644
--- a/src/main/java/org/codehaus/groovy/transform/AbstractASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/AbstractASTTransformation.java
@@ -438,15 +438,15 @@ public abstract class AbstractASTTransformation 
implements Opcodes, ASTTransform
         }
     }
 
-    protected boolean checkPropertyList(ClassNode cNode, List<String> 
propertyNameList, String listName, AnnotationNode anno, String typeName, 
boolean includeFields) {
+    public boolean checkPropertyList(ClassNode cNode, List<String> 
propertyNameList, String listName, AnnotationNode anno, String typeName, 
boolean includeFields) {
         return checkPropertyList(cNode, propertyNameList, listName, anno, 
typeName, includeFields, false, false);
     }
 
-    protected boolean checkPropertyList(ClassNode cNode, List<String> 
propertyNameList, String listName, AnnotationNode anno, String typeName, 
boolean includeFields, boolean includeSuperProperties, boolean allProperties) {
+    public boolean checkPropertyList(ClassNode cNode, List<String> 
propertyNameList, String listName, AnnotationNode anno, String typeName, 
boolean includeFields, boolean includeSuperProperties, boolean allProperties) {
         return checkPropertyList(cNode, propertyNameList, listName, anno, 
typeName, includeFields, includeSuperProperties, allProperties, false, false);
     }
 
-    protected boolean checkPropertyList(ClassNode cNode, List<String> 
propertyNameList, String listName, AnnotationNode anno, String typeName, 
boolean includeFields, boolean includeSuperProperties, boolean allProperties, 
boolean includeSuperFields, boolean includeStatic) {
+    public boolean checkPropertyList(ClassNode cNode, List<String> 
propertyNameList, String listName, AnnotationNode anno, String typeName, 
boolean includeFields, boolean includeSuperProperties, boolean allProperties, 
boolean includeSuperFields, boolean includeStatic) {
         if (propertyNameList == null || propertyNameList.isEmpty()) {
             return true;
         }

http://git-wip-us.apache.org/repos/asf/groovy/blob/e2f4ceb5/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 ad4001f..1cd79c6 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
@@ -18,14 +18,16 @@
  */
 package org.codehaus.groovy.transform;
 
+import groovy.lang.GroovyClassLoader;
 import groovy.lang.MetaClass;
 import groovy.lang.MissingPropertyException;
+import groovy.transform.CompilationUnitAware;
 import groovy.transform.ImmutableBase;
+import groovy.transform.options.PropertyHandler;
 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;
-import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.ConstructorNode;
@@ -34,11 +36,9 @@ import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.PropertyNode;
 import org.codehaus.groovy.ast.VariableScope;
 import org.codehaus.groovy.ast.expr.ConstantExpression;
-import org.codehaus.groovy.ast.expr.Expression;
-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.Statement;
+import org.codehaus.groovy.control.CompilationUnit;
 import org.codehaus.groovy.control.CompilePhase;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
@@ -53,19 +53,13 @@ import java.util.List;
 import java.util.Map;
 
 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;
 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.constX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
@@ -81,7 +75,6 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.neX;
 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.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.varX;
@@ -90,7 +83,9 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
  * Handles generation of code for the @Immutable annotation.
  */
 @GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
-public class ImmutableASTTransformation extends AbstractASTTransformation {
+public class ImmutableASTTransformation extends AbstractASTTransformation 
implements CompilationUnitAware {
+    private CompilationUnit compilationUnit;
+
     private static final Class<? extends Annotation> MY_CLASS = 
ImmutableBase.class;
     public static final ClassNode MY_TYPE = makeWithoutCaching(MY_CLASS, 
false);
     private static final String MY_TYPE_NAME = MY_TYPE.getNameWithoutPackage();
@@ -101,29 +96,36 @@ public class ImmutableASTTransformation extends 
AbstractASTTransformation {
     private static final ClassNode HMAP_TYPE = 
makeWithoutCaching(HashMap.class, false);
     public static final String IMMUTABLE_SAFE_FLAG = "Immutable.Safe";
 
+    @Override
+    public String getAnnotationName() {
+        return MY_TYPE_NAME;
+    }
+
     public void visit(ASTNode[] nodes, SourceUnit source) {
         init(nodes, source);
         AnnotatedNode parent = (AnnotatedNode) nodes[1];
-        AnnotationNode node = (AnnotationNode) nodes[0];
-        if (!MY_TYPE.equals(node.getClassNode())) return;
+        AnnotationNode anno = (AnnotationNode) nodes[0];
+        if (!MY_TYPE.equals(anno.getClassNode())) return;
 
         if (parent instanceof ClassNode) {
-            doMakeImmutable((ClassNode) parent, node);
+            final GroovyClassLoader classLoader = compilationUnit != null ? 
compilationUnit.getTransformLoader() : source.getClassLoader();
+            final PropertyHandler handler = 
PropertyHandler.createPropertyHandler(this, classLoader, (ClassNode) parent);
+            if (handler == null) return;
+            if (!handler.validateAttributes(this, anno)) return;
+            doMakeImmutable((ClassNode) parent, anno, handler);
         }
     }
 
-    private void doMakeImmutable(ClassNode cNode, AnnotationNode node) {
+    private void doMakeImmutable(ClassNode cNode, AnnotationNode node, 
PropertyHandler handler) {
         List<PropertyNode> newProperties = new ArrayList<PropertyNode>();
-        final List<String> knownImmutables = getKnownImmutables(this, node);
 
         String cName = cNode.getName();
         if (!checkNotInterface(cNode, MY_TYPE_NAME)) return;
-        if (!checkPropertyList(cNode, knownImmutables, "knownImmutables", 
node, "immutable class", false)) return;
         makeClassFinal(this, cNode);
 
         final List<PropertyNode> pList = getInstanceProperties(cNode);
         for (PropertyNode pNode : pList) {
-            adjustPropertyForImmutability(pNode, newProperties);
+            adjustPropertyForImmutability(pNode, newProperties, handler);
         }
         for (PropertyNode pNode : newProperties) {
             cNode.getProperties().remove(pNode);
@@ -162,36 +164,6 @@ public class ImmutableASTTransformation extends 
AbstractASTTransformation {
         return false;
     }
 
-    static void doAddConstructor(final ClassNode cNode, final ConstructorNode 
constructorNode) {
-        cNode.addConstructor(constructorNode);
-        // GROOVY-5814: Immutable is not compatible with @CompileStatic
-        Parameter argsParam = null;
-        for (Parameter p : constructorNode.getParameters()) {
-            if ("args".equals(p.getName())) {
-                argsParam = p;
-                break;
-            }
-        }
-        if (argsParam != null) {
-            final Parameter arg = argsParam;
-            ClassCodeVisitorSupport variableExpressionFix = new 
ClassCodeVisitorSupport() {
-                @Override
-                protected SourceUnit getSourceUnit() {
-                    return cNode.getModule().getContext();
-                }
-
-                @Override
-                public void visitVariableExpression(final VariableExpression 
expression) {
-                    super.visitVariableExpression(expression);
-                    if ("args".equals(expression.getName())) {
-                        expression.setAccessedVariable(arg);
-                    }
-                }
-            };
-            variableExpressionFix.visitConstructor(constructorNode);
-        }
-    }
-
     private static void makeClassFinal(AbstractASTTransformation xform, 
ClassNode cNode) {
         int modifiers = cNode.getModifiers();
         if ((modifiers & ACC_FINAL) == 0) {
@@ -220,41 +192,6 @@ public class ImmutableASTTransformation extends 
AbstractASTTransformation {
         return false;
     }
 
-    @Deprecated
-    static List<PropertyNode> getProperties(ClassNode cNode, boolean 
includeSuperProperties, boolean allProperties) {
-        List<PropertyNode> list = getInstanceProperties(cNode);
-        if (includeSuperProperties) {
-            ClassNode next = cNode.getSuperClass();
-            while (next != null) {
-                List<PropertyNode> tail = list;
-                list = getInstanceProperties(next);
-                list.addAll(tail);
-                next = next.getSuperClass();
-            }
-        }
-        return list;
-    }
-
-    @Deprecated
-    static void createConstructorOrdered(ClassNode cNode, List<PropertyNode> 
list) {
-        final MapExpression argMap = new MapExpression();
-        final Parameter[] orderedParams = new Parameter[list.size()];
-        int index = 0;
-        for (PropertyNode pNode : list) {
-            Parameter param = new Parameter(pNode.getField().getType(), 
pNode.getField().getName());
-            orderedParams[index++] = param;
-            argMap.addMapEntryExpression(constX(pNode.getName()), 
varX(pNode.getName()));
-        }
-        final BlockStatement orderedBody = new BlockStatement();
-        orderedBody.addStatement(stmt(ctorX(ClassNode.THIS, 
args(castX(HMAP_TYPE, argMap)))));
-        doAddConstructor(cNode, new ConstructorNode(ACC_PUBLIC, orderedParams, 
ClassNode.EMPTY_ARRAY, orderedBody));
-    }
-
-    private static Statement createGetterBodyDefault(FieldNode fNode) {
-        final Expression fieldExpr = varX(fNode);
-        return stmt(fieldExpr);
-    }
-
     private static void ensureNotPublic(AbstractASTTransformation xform, 
String cNode, FieldNode fNode) {
         String fName = fNode.getName();
         // TODO: do we need to lock down things like: $ownClass
@@ -288,48 +225,20 @@ public class ImmutableASTTransformation extends 
AbstractASTTransformation {
     }
 
     static boolean makeImmutable(ClassNode cNode) {
-        List<AnnotationNode> annotations = 
cNode.getAnnotations(ImmutablePropertyUtils.IMMUTABLE_BASE_TYPE);
+        List<AnnotationNode> annotations = 
cNode.getAnnotations(ImmutablePropertyUtils.IMMUTABLE_OPTIONS_TYPE);
         AnnotationNode annoImmutable = annotations.isEmpty() ? null : 
annotations.get(0);
         return annoImmutable != null;
     }
 
-    private static void adjustPropertyForImmutability(PropertyNode pNode, 
List<PropertyNode> newNodes) {
+    private static void adjustPropertyForImmutability(PropertyNode pNode, 
List<PropertyNode> newNodes, PropertyHandler handler) {
         final FieldNode fNode = pNode.getField();
         fNode.setModifiers((pNode.getModifiers() & (~ACC_PUBLIC)) | ACC_FINAL 
| ACC_PRIVATE);
-        adjustPropertyNode(pNode, createGetterBody(fNode));
-        newNodes.add(pNode);
-    }
-
-    private static void adjustPropertyNode(PropertyNode pNode, Statement 
getterBody) {
         pNode.setSetterBlock(null);
-        pNode.setGetterBlock(getterBody);
-    }
-
-    private static Statement createGetterBody(FieldNode fNode) {
-        BlockStatement body = new BlockStatement();
-        final ClassNode fieldType = fNode.getType();
-        final Statement statement;
-        if (fieldType.isArray() || implementsCloneable(fieldType)) {
-            statement = createGetterBodyArrayOrCloneable(fNode);
-        } else if (derivesFromDate(fieldType)) {
-            statement = createGetterBodyDate(fNode);
-        } else {
-            statement = createGetterBodyDefault(fNode);
+        Statement getter = handler.createPropGetter(pNode);
+        if (getter != null) {
+            pNode.setGetterBlock(getter);
         }
-        body.addStatement(statement);
-        return body;
-    }
-
-    private static Statement createGetterBodyArrayOrCloneable(FieldNode fNode) 
{
-        final Expression fieldExpr = varX(fNode);
-        final Expression expression = cloneArrayOrCloneableExpr(fieldExpr, 
fNode.getType());
-        return safeExpression(fieldExpr, expression);
-    }
-
-    private static Statement createGetterBodyDate(FieldNode fNode) {
-        final Expression fieldExpr = varX(fNode);
-        final Expression expression = cloneDateExpr(fieldExpr);
-        return safeExpression(fieldExpr, expression);
+        newNodes.add(pNode);
     }
 
     private static Statement createCheckForProperty(final PropertyNode pNode) {
@@ -530,4 +439,9 @@ public class ImmutableASTTransformation extends 
AbstractASTTransformation {
                 throw new MissingPropertyException(k, instance.getClass());
         }
     }
+
+    @Override
+    public void setCompilationUnit(CompilationUnit unit) {
+        this.compilationUnit = unit;
+    }
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/e2f4ceb5/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 3859f72..aa995ef 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
@@ -21,11 +21,12 @@ package org.codehaus.groovy.transform;
 import groovy.lang.GroovyClassLoader;
 import groovy.transform.CompilationUnitAware;
 import groovy.transform.MapConstructor;
-import groovy.transform.construction.PropertyHandler;
+import groovy.transform.options.PropertyHandler;
 import org.codehaus.groovy.ast.ASTNode;
 import org.codehaus.groovy.ast.AnnotatedNode;
 import org.codehaus.groovy.ast.AnnotationNode;
 import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.ConstructorNode;
 import org.codehaus.groovy.ast.DynamicVariable;
@@ -59,7 +60,7 @@ import static 
org.codehaus.groovy.ast.tools.GeneralUtils.getAllProperties;
 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.stmt;
-import static 
org.codehaus.groovy.transform.ImmutableASTTransformation.doAddConstructor;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
 
 /**
  * Handles generation of code for the @MapConstructor annotation.
@@ -93,7 +94,6 @@ public class MapConstructorASTTransformation extends 
AbstractASTTransformation i
             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 includeStatic = memberHasValue(anno, "includeStatic", 
true);
             boolean allProperties = memberHasValue(anno, "allProperties", 
true);
             boolean noArg = memberHasValue(anno, "noArg", true);
@@ -102,12 +102,12 @@ public class MapConstructorASTTransformation extends 
AbstractASTTransformation i
             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))
+            if (!checkPropertyList(cNode, includes, "includes", anno, 
MY_TYPE_NAME, includeFields, includeSuperProperties, allProperties))
                 return;
-            if (!checkPropertyList(cNode, excludes, "excludes", anno, 
MY_TYPE_NAME, includeFields, includeSuperProperties, false))
+            if (!checkPropertyList(cNode, excludes, "excludes", anno, 
MY_TYPE_NAME, includeFields, includeSuperProperties, allProperties))
                 return;
             final GroovyClassLoader classLoader = compilationUnit != null ? 
compilationUnit.getTransformLoader() : source.getClassLoader();
-            final PropertyHandler handler = 
PropertyHandler.createPropertyHandler(this, anno, classLoader);
+            final PropertyHandler handler = 
PropertyHandler.createPropertyHandler(this, classLoader, cNode);
             if (handler == null) return;
             if (!handler.validateAttributes(this, anno)) return;
 
@@ -179,11 +179,44 @@ public class MapConstructorASTTransformation extends 
AbstractASTTransformation i
         }
     }
 
+    private static void doAddConstructor(final ClassNode cNode, final 
ConstructorNode constructorNode) {
+        cNode.addConstructor(constructorNode);
+        // GROOVY-5814: Immutable is not compatible with @CompileStatic
+        Parameter argsParam = null;
+        for (Parameter p : constructorNode.getParameters()) {
+            if ("args".equals(p.getName())) {
+                argsParam = p;
+                break;
+            }
+        }
+        if (argsParam != null) {
+            final Parameter arg = argsParam;
+            ClassCodeVisitorSupport variableExpressionFix = new 
ClassCodeVisitorSupport() {
+                @Override
+                protected SourceUnit getSourceUnit() {
+                    return cNode.getModule().getContext();
+                }
+
+                @Override
+                public void visitVariableExpression(final VariableExpression 
expression) {
+                    super.visitVariableExpression(expression);
+                    if ("args".equals(expression.getName())) {
+                        expression.setAccessedVariable(arg);
+                    }
+                }
+            };
+            variableExpressionFix.visitConstructor(constructorNode);
+        }
+    }
+
     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;
-            handler.createStatement(xform, anno, inner, cNode, pNode, map);
+            Statement propInit = handler.createPropInit(xform, anno, cNode, 
pNode, map);
+            if (propInit != null) {
+                inner.addStatement(propInit);
+            }
         }
     }
 
@@ -201,8 +234,8 @@ public class MapConstructorASTTransformation extends 
AbstractASTTransformation i
                     ce.getCode().visit(this);
                 } else if (exp instanceof VariableExpression) {
                     VariableExpression ve = (VariableExpression) exp;
-                    if (ve.getName().equals("args") && 
ve.getAccessedVariable() instanceof DynamicVariable) {
-                        VariableExpression newVe = new VariableExpression(new 
Parameter(MAP_TYPE, "args"));
+                    if ("args".equals(ve.getName()) && 
ve.getAccessedVariable() instanceof DynamicVariable) {
+                        VariableExpression newVe = varX(param(MAP_TYPE, 
"args"));
                         newVe.setSourcePosition(ve);
                         return newVe;
                     }

http://git-wip-us.apache.org/repos/asf/groovy/blob/e2f4ceb5/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 13decb9..d225dfb 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
@@ -22,7 +22,7 @@ import groovy.lang.GroovyClassLoader;
 import groovy.transform.CompilationUnitAware;
 import groovy.transform.MapConstructor;
 import groovy.transform.TupleConstructor;
-import groovy.transform.construction.PropertyHandler;
+import groovy.transform.options.PropertyHandler;
 import org.codehaus.groovy.ast.ASTNode;
 import org.codehaus.groovy.ast.AnnotatedNode;
 import org.codehaus.groovy.ast.AnnotationNode;
@@ -60,14 +60,12 @@ 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.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.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.ifElseS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
@@ -133,7 +131,7 @@ public class TupleConstructorASTTransformation extends 
AbstractASTTransformation
             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);
+            final PropertyHandler handler = 
PropertyHandler.createPropertyHandler(this, classLoader, cNode);
             if (handler == null) return;
             if (!handler.validateAttributes(this, anno)) return;
 
@@ -211,11 +209,13 @@ public class TupleConstructorASTTransformation extends 
AbstractASTTransformation
             FieldNode fNode = pNode.getField();
             if (shouldSkipUndefinedAware(name, excludes, includes, allNames)) 
continue;
             params.add(createParam(fNode, name, defaults, xform, 
makeImmutable));
-            boolean hasSetter = cNode.getProperty(name) != null && 
!fNode.isFinal();
             if (callSuper) {
                 superParams.add(varX(name));
             } else if (!superInPre && !specialNamedArgCase) {
-                handler.createStatement(xform, anno, body, cNode, pNode, null);
+                Statement propInit = handler.createPropInit(xform, anno, 
cNode, pNode, null);
+                if (propInit != null) {
+                    body.addStatement(propInit);
+                }
             }
         }
         if (callSuper) {
@@ -231,7 +231,10 @@ public class TupleConstructorASTTransformation extends 
AbstractASTTransformation
             if (shouldSkipUndefinedAware(name, excludes, includes, allNames)) 
continue;
             Parameter nextParam = createParam(fNode, name, defaults, xform, 
makeImmutable);
             params.add(nextParam);
-            handler.createStatement(xform, anno, body, cNode, pNode, null);
+            Statement propInit = handler.createPropInit(xform, anno, cNode, 
pNode, null);
+            if (propInit != null) {
+                body.addStatement(propInit);
+            }
         }
 
         if (post != null) {
@@ -263,7 +266,7 @@ public class TupleConstructorASTTransformation extends 
AbstractASTTransformation
             ClassNode firstParamType = params.get(0).getType();
             if (params.size() > 1 || 
firstParamType.equals(ClassHelper.OBJECT_TYPE)) {
                 String message = "The class " + cNode.getName() + " was 
incorrectly initialized via the map constructor with null.";
-                addSpecialMapConstructors(cNode, false, message);
+                addSpecialMapConstructors(cNode, message, false);
             }
         }
     }
@@ -290,7 +293,7 @@ public class TupleConstructorASTTransformation extends 
AbstractASTTransformation
         return initialExp;
     }
 
-    public static void addSpecialMapConstructors(ClassNode cNode, boolean 
addNoArg, String message) {
+    public static void addSpecialMapConstructors(ClassNode cNode, String 
message, boolean addNoArg) {
         Parameter[] parameters = params(new Parameter(LHMAP_TYPE, 
"__namedArgs"));
         BlockStatement code = new BlockStatement();
         VariableExpression namedArgs = varX("__namedArgs");

http://git-wip-us.apache.org/repos/asf/groovy/blob/e2f4ceb5/src/spec/doc/core-metaprogramming.adoc
----------------------------------------------------------------------
diff --git a/src/spec/doc/core-metaprogramming.adoc 
b/src/spec/doc/core-metaprogramming.adoc
index 57f726f..7230e11 100644
--- a/src/spec/doc/core-metaprogramming.adoc
+++ b/src/spec/doc/core-metaprogramming.adoc
@@ -942,9 +942,13 @@ expecting exactly one constructor, e.g. injection 
frameworks or JUnit parameteri
 
 ===== Immutability support
 
-If the `@ImmutableBase` annotation (normally added by the `@Immutable` 
meta-annotation) is also found on the class with the `@TupleConstructor` 
annotation,
-then the generated constructor will have all the necessary logic required for 
immutable classes (defensive copy in, cloning, etc.). Some of the
-annotation attributes won't be supported in this case.
+If the `@PropertyOptions` annotation is also found on the class with the 
`@TupleConstructor` annotation,
+then the generated constructor may contain custom property handling logic.
+The `propertyHandler` attribute on the `@PropertyOptions` annotation could for 
instance be set to
+`ImmutablePropertyHandler` which will result in the addition of the necessary 
logic for immutable classes
+(defensive copy in, cloning, etc.). This normally would happen automatically 
behind the scenes when you use
+the `@Immutable` meta-annotation.
+Some of the annotation attributes might not be supported by all property 
handlers.
 
 ===== Customization options
 
@@ -1702,9 +1706,11 @@ The `@Immutable` meta-annotation combines the following 
annotations:
 
 * <<xform-ToString,@ToString>>
 * <<xform-EqualsAndHashCode,@EqualsAndHashCode>>
-* <<xform-ImmutableBase,@ImmutableBase>>
 * <<xform-TupleConstructor,@TupleConstructor>>
 * <<xform-MapConstructor,@MapConstructor>>
+* <<xform-ImmutableBase,@ImmutableBase>>
+* <<xform-ImmutableOptions,@ImmutableOptions>>
+* <<xform-PropertyOptions,@PropertyOptions>>
 * <<xform-KnownImmutable,@KnownImmutable>>
 
 The `@Immutable` meta-annotation simplifies the creation of immutable classes. 
Immutable classes are useful
@@ -1725,7 +1731,7 @@ such as defensive copy in and defensive copy out for any 
mutable properties with
 and property getters. Between `@ImmutableBase`, `@MapConstructor` and 
`@TupleConstructor` properties
 are either identified as immutable or the special coding for numerous known 
cases is handled automatically.
 Various mechanisms are provided for you to extend the handled property types 
which are allowed. See
-`@ImmutableBase` and `@KnownImmutable` for details.
+`@ImmutableOptions` and `@KnownImmutable` for details.
 
 The results of applying `@Immutable` to a class are pretty similar to those of
 applying the <<xform-Canonical,@Canonical>> meta-annotation but the generated 
class will have extra
@@ -1740,15 +1746,35 @@ it aggregates. See those annotations for more details.
 ===== `@groovy.transform.ImmutableBase`
 
 Immutable classes generated with `@ImmutableBase` are automatically made 
final. Also, the type of each property is checked
-and various checks are made on the class, for example, public instance fields 
currently aren't allowed.
+and various checks are made on the class, for example, public instance fields 
currently aren't allowed. It also generates
+a `copyWith` constructor if desired.
 
-For a class to be immutable, you have to
-make sure that properties are of an immutable type (primitive or boxed types), 
of a known-immutable type or another
-class annotated with `@KnownImmutable` (which includes those annotated with 
the `@Immutable` meta-annotation).
+The following annotation attribute is supported:
 
-Since `@ImmutableBase` relies on a predefined list of known immutable classes 
(like `java.net.URI` or `java.lang.String`
-and fails if you use a type which is not in that list, you are allowed to 
instruct the transformation that some types
-are deemed immutable thanks to the following annotation attributes:
+[cols="1,1,2,3a",options="header"]
+|=======================================================================
+|Attribute|Default value|Description|Example
+|copyWith|false|A boolean whether to generate a `copyWith( Map )` method.|
+[source,groovy]
+----
+include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=immutable_example_copyWith,indent=0]
+----
+|=======================================================================
+
+[[xform-PropertyOptions]]
+===== `@groovy.transform.PropertyOptions`
+
+This annotation allows you to specify a custom property handler to be used by 
transformations
+during class construction. It is ignored by the main Groovy compiler but is 
referenced by other transformations
+like `@TupleConstructor`, `@MapConstructor`, and `@ImmutableBase`. It is 
frequently used behind the
+scenes by the `@Immutable` meta-annotation.
+
+[[xform-ImumtableOptions]]
+===== `@groovy.transform.ImmutableOptions`
+
+Groovy's immutability support relies on a predefined list of known immutable 
classes (like `java.net.URI` or `java.lang.String`
+and fails if you use a type which is not in that list, you are allowed to add 
to the list of known immutable types
+thanks to the following annotation attributes of the `@ImmutableOptions` 
annotation:
 
 [cols="1,1,2,3a",options="header"]
 |=======================================================================
@@ -1763,12 +1789,6 @@ 
include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=imm
 ----
 
include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=immutable_example_knownimmutables,indent=0]
 ----
-----
-|copyWith|false|A boolean whether to generate a `copyWith( Map )` method.|
-[source,groovy]
-----
-include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=immutable_example_copyWith,indent=0]
-----
 |=======================================================================
 
 If you deem a type as immutable and it isn't one of the ones automatically 
handled, then it is up to you
@@ -1780,7 +1800,7 @@ to correctly code that class to ensure immutability.
 The `@KnownImmutable` annotation isn't actually one that triggers any AST 
transformations. It is simply
 a marker annotation. You can annotate your classes with the annotation 
(including Java classes) and they
 will be recognized as acceptable types for members within an immutable class. 
This saves you having to
-explicitly use the `knownImmutables` or `knownImmutableClasses` annotation 
attributes from `@ImmutableBase`.
+explicitly use the `knownImmutables` or `knownImmutableClasses` annotation 
attributes from `@ImmutableOptions`.
 
 [[xform-Memoized]]
 ===== `@groovy.transform.Memoized`

http://git-wip-us.apache.org/repos/asf/groovy/blob/e2f4ceb5/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 2d817e5..e8a6ddf 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(propertyHandler = 
groovy.transform.construction.LegacyHashMapPropertyHandler, noArg = false)
+            import groovy.transform.options.LegacyHashMapPropertyHandler
+            @Immutable(propertyHandler = LegacyHashMapPropertyHandler, noArg = 
false)
             final class HasHashMap {
                 HashMap map = [d:4]
             }

Reply via email to