groovy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pa...@apache.org
Subject [1/2] groovy git commit: GROOVY-8477: @Immutable-related transformations should be more configurable (further refactoring)
Date Fri, 16 Feb 2018 10:28:00 GMT
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]
             }


Mime
View raw message