You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2015/08/06 05:10:22 UTC

incubator-groovy git commit: GROOVY-7529: Fix up transforms (apart from TupleConstructor) which are affected by empty includes default

Repository: incubator-groovy
Updated Branches:
  refs/heads/master e2d765087 -> b6ff4efed


GROOVY-7529: Fix up transforms (apart from TupleConstructor) which are affected by empty includes default


Project: http://git-wip-us.apache.org/repos/asf/incubator-groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-groovy/commit/b6ff4efe
Tree: http://git-wip-us.apache.org/repos/asf/incubator-groovy/tree/b6ff4efe
Diff: http://git-wip-us.apache.org/repos/asf/incubator-groovy/diff/b6ff4efe

Branch: refs/heads/master
Commit: b6ff4efedae7598742a6d536cff2e6c6e1d930d3
Parents: e2d7650
Author: paulk <pa...@asert.com.au>
Authored: Thu Jul 30 22:44:57 2015 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Thu Aug 6 13:09:36 2015 +1000

----------------------------------------------------------------------
 src/main/groovy/lang/Delegate.java              |  18 ++--
 .../groovy/transform/EqualsAndHashCode.java     |   5 +-
 src/main/groovy/transform/MapConstructor.java   |   4 +-
 src/main/groovy/transform/Sortable.java         |   4 +-
 src/main/groovy/transform/ToString.java         |   4 +-
 src/main/groovy/transform/builder/Builder.java  |   4 +-
 .../transform/builder/DefaultStrategy.java      |   6 +-
 .../transform/builder/ExternalStrategy.java     |  11 +-
 .../transform/builder/InitializerStrategy.java  |   5 +-
 .../transform/builder/SimpleStrategy.java       |  11 +-
 .../transform/AbstractASTTransformation.java    | 105 ++++++++++++++++++-
 .../transform/AutoCloneASTTransformation.java   |   8 +-
 .../transform/BuilderASTTransformation.java     |  23 ++--
 .../transform/DelegateASTTransformation.java    |  15 +--
 .../EqualsAndHashCodeASTTransformation.java     |   6 +-
 .../ExternalizeMethodsASTTransformation.java    |   6 +-
 .../ExternalizeVerifierASTTransformation.java   |   4 +-
 .../MapConstructorASTTransformation.java        |   6 +-
 .../transform/SortableASTTransformation.java    |  12 +--
 .../transform/ToStringASTTransformation.java    |   6 +-
 src/spec/doc/core-metaprogramming.adoc          |  44 ++++++--
 .../test/CodeGenerationASTTransformsTest.groovy |  30 ++++++
 22 files changed, 264 insertions(+), 73 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/groovy/lang/Delegate.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/lang/Delegate.java b/src/main/groovy/lang/Delegate.java
index a519575..6513129 100644
--- a/src/main/groovy/lang/Delegate.java
+++ b/src/main/groovy/lang/Delegate.java
@@ -18,6 +18,7 @@
  */
 package groovy.lang;
 
+import groovy.transform.Undefined;
 import org.codehaus.groovy.transform.GroovyASTTransformationClass;
 
 import java.lang.annotation.ElementType;
@@ -164,7 +165,8 @@ public @interface Delegate {
 
     /**
      * List of method and/or property names to exclude when delegating.
-     * Must not be used if 'includes' is used. For convenience, a String with comma separated names
+     * Only one of 'includes', 'includeTypes', 'excludes' or 'excludeTypes' should be used.
+     * For convenience, a String with comma separated names
      * can be used in addition to an array (using Groovy's literal list notation) of String values.
      * If interfaces is true (the default), you will need to manually supply any methods excluded
      * from delegation that are required for the interface.
@@ -184,20 +186,24 @@ public @interface Delegate {
 
     /**
      * List of method and/or property names to include when delegating.
-     * Must not be used if 'excludes' is used. For convenience, a String with comma separated names
+     * Only one of 'includes', 'includeTypes', 'excludes' or 'excludeTypes' should be used.
+     * For convenience, a String with comma separated names
      * can be used in addition to an array (using Groovy's literal list notation) of String values.
+     * The default value is a special marker value indicating that no includes are defined; all fields
+     * are included if 'includes' remains undefined and 'excludes' is explicitly or implicitly an empty list.
      * If interfaces is true (the default), you will need to manually supply any methods not included
      * via delegation that are required for the interface.
      * @since 2.2.0
      */
-    String[] includes() default {};
+    String[] includes() default {Undefined.STRING};
 
     /**
-     * List of interfaces containing method signatures to exclude when delegating.
-     * Only one of 'includes', 'includeTypes', 'excludes', 'excludeTypes' should be used.
+     * List of interfaces containing method signatures to include when delegating.
+     * Only one of 'includes', 'includeTypes', 'excludes' or 'excludeTypes' should be used.
+     * The default value is a special marker value indicating that no includeTypes are defined.
      * If interfaces is true (the default), you will need to manually supply any methods excluded
      * from delegation that are required for the interface.
      * @since 2.3.0
      */
-    Class[] includeTypes() default {};
+    Class[] includeTypes() default {Undefined.CLASS.class};
 }

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/groovy/transform/EqualsAndHashCode.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/EqualsAndHashCode.java b/src/main/groovy/transform/EqualsAndHashCode.java
index 54315da..51bd1b5 100644
--- a/src/main/groovy/transform/EqualsAndHashCode.java
+++ b/src/main/groovy/transform/EqualsAndHashCode.java
@@ -213,8 +213,11 @@ public @interface EqualsAndHashCode {
      * List of field and/or property names to include within the equals and hashCode calculations.
      * Must not be used if 'excludes' is used. For convenience, a String with comma separated names
      * can be used in addition to an array (using Groovy's literal list notation) of String values.
+     * The default value is a special marker value indicating that no includes are defined; all fields
+     * and/or properties are included if 'includes' remains undefined and 'excludes' is explicitly or
+     * implicitly an empty list.
      */
-    String[] includes() default {};
+    String[] includes() default {Undefined.STRING};
 
     /**
      * Whether to cache hashCode calculations. You should only set this to true if

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/groovy/transform/MapConstructor.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/MapConstructor.java b/src/main/groovy/transform/MapConstructor.java
index ea3632b..8388643 100644
--- a/src/main/groovy/transform/MapConstructor.java
+++ b/src/main/groovy/transform/MapConstructor.java
@@ -83,8 +83,10 @@ public @interface MapConstructor {
      * List of field and/or property names to include within the constructor.
      * Must not be used if 'excludes' is used. For convenience, a String with comma separated names
      * can be used in addition to an array (using Groovy's literal list notation) of String values.
+     * The default value is a special marker value indicating that no includes are defined; all fields and/or properties
+     * are included if 'includes' remains undefined and 'excludes' is explicitly or implicitly an empty list.
      */
-    String[] includes() default {};
+    String[] includes() default {Undefined.STRING};
 
     /**
      * Include fields in the constructor.

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/groovy/transform/Sortable.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/Sortable.java b/src/main/groovy/transform/Sortable.java
index fa1c22f..744d016 100644
--- a/src/main/groovy/transform/Sortable.java
+++ b/src/main/groovy/transform/Sortable.java
@@ -58,8 +58,10 @@ public @interface Sortable {
     /**
      * Property names to include in the comparison algorithm.
      * Must not be used if 'excludes' is used.
+     * The default value is a special marker value indicating that no includes are defined; all properties
+     * are included if 'includes' remains undefined and 'excludes' is explicitly or implicitly an empty list.
      */
-    String[] includes() default {};
+    String[] includes() default {Undefined.STRING};
 
     /**
      * Property names to exclude in the comparison algorithm.

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/groovy/transform/ToString.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/ToString.java b/src/main/groovy/transform/ToString.java
index 6fc252c..83ff809 100644
--- a/src/main/groovy/transform/ToString.java
+++ b/src/main/groovy/transform/ToString.java
@@ -135,8 +135,10 @@ public @interface ToString {
      * List of field and/or property names to include within the generated toString.
      * Must not be used if 'excludes' is used. For convenience, a String with comma separated names
      * can be used in addition to an array (using Groovy's literal list notation) of String values.
+     * The default value is a special marker value indicating that no includes are defined; all fields and/or properties
+     * are included if 'includes' remains undefined and 'excludes' is explicitly or implicitly an empty list.
      */
-    String[] includes() default {};
+    String[] includes() default {Undefined.STRING};
 
     /**
      * Whether to include the toString() of super in the generated toString.

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/groovy/transform/builder/Builder.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/builder/Builder.java b/src/main/groovy/transform/builder/Builder.java
index f2731fa..58df280 100644
--- a/src/main/groovy/transform/builder/Builder.java
+++ b/src/main/groovy/transform/builder/Builder.java
@@ -123,8 +123,10 @@ public @interface Builder {
      * List of field and/or property names to include within the generated builder methods.
      * Must not be used if 'excludes' is used. For convenience, a String with comma separated names
      * can be used in addition to an array (using Groovy's literal list notation) of String values.
+     * The default value is a special marker value indicating that no includes are defined; all fields
+     * are included if includes remains undefined and excludes is explicitly or implicitly an empty list.
      */
-    String[] includes() default {};
+    String[] includes() default {Undefined.STRING};
 
     /**
      * By default, properties are set directly using their respective field.

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/groovy/transform/builder/DefaultStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/builder/DefaultStrategy.java b/src/main/groovy/transform/builder/DefaultStrategy.java
index 1c90adc..ca3cf08 100644
--- a/src/main/groovy/transform/builder/DefaultStrategy.java
+++ b/src/main/groovy/transform/builder/DefaultStrategy.java
@@ -18,6 +18,7 @@
  */
 package groovy.transform.builder;
 
+import groovy.transform.Undefined;
 import org.codehaus.groovy.ast.AnnotatedNode;
 import org.codehaus.groovy.ast.AnnotationNode;
 import org.codehaus.groovy.ast.ClassNode;
@@ -55,6 +56,7 @@ import static org.codehaus.groovy.ast.tools.GenericsUtils.extractSuperClassGener
 import static org.codehaus.groovy.ast.tools.GenericsUtils.newClass;
 import static org.codehaus.groovy.transform.AbstractASTTransformation.getMemberStringValue;
 import static org.codehaus.groovy.transform.AbstractASTTransformation.shouldSkip;
+import static org.codehaus.groovy.transform.AbstractASTTransformation.shouldSkipUndefinedAware;
 import static org.codehaus.groovy.transform.BuilderASTTransformation.NO_EXCEPTIONS;
 import static org.codehaus.groovy.transform.BuilderASTTransformation.NO_PARAMS;
 import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
@@ -194,7 +196,9 @@ public class DefaultStrategy extends BuilderASTTransformation.AbstractBuilderStr
     public void buildClass(BuilderASTTransformation transform, ClassNode buildee, AnnotationNode anno) {
         List<String> excludes = new ArrayList<String>();
         List<String> includes = new ArrayList<String>();
+        includes.add(Undefined.STRING);
         if (!getIncludeExclude(transform, anno, buildee, excludes, includes)) return;
+        if (includes.size() == 1 && Undefined.isUndefined(includes.get(0))) includes = null;
         ClassNode builder = createBuilder(anno, buildee);
         createBuilderFactoryMethod(anno, buildee, builder);
         List<FieldNode> fields = getInstancePropertyFields(buildee);
@@ -284,7 +288,7 @@ public class DefaultStrategy extends BuilderASTTransformation.AbstractBuilderStr
     private static List<FieldNode> selectFieldsFromExistingClass(List<FieldNode> fieldNodes, List<String> includes, List<String> excludes) {
         List<FieldNode> fields = new ArrayList<FieldNode>();
         for (FieldNode fNode : fieldNodes) {
-            if (shouldSkip(fNode.getName(), excludes, includes)) continue;
+            if (shouldSkipUndefinedAware(fNode.getName(), excludes, includes)) continue;
             fields.add(fNode);
         }
         return fields;

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/groovy/transform/builder/ExternalStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/builder/ExternalStrategy.java b/src/main/groovy/transform/builder/ExternalStrategy.java
index 988e176..2cef050 100644
--- a/src/main/groovy/transform/builder/ExternalStrategy.java
+++ b/src/main/groovy/transform/builder/ExternalStrategy.java
@@ -18,6 +18,7 @@
  */
 package groovy.transform.builder;
 
+import groovy.transform.Undefined;
 import org.codehaus.groovy.ast.AnnotatedNode;
 import org.codehaus.groovy.ast.AnnotationNode;
 import org.codehaus.groovy.ast.ClassHelper;
@@ -112,7 +113,9 @@ public class ExternalStrategy extends BuilderASTTransformation.AbstractBuilderSt
         }
         List<String> excludes = new ArrayList<String>();
         List<String> includes = new ArrayList<String>();
+        includes.add(Undefined.STRING);
         if (!getIncludeExclude(transform, anno, buildee, excludes, includes)) return;
+        if (includes.size() == 1 && Undefined.isUndefined(includes.get(0))) includes = null;
         if (unsupportedAttribute(transform, anno, "builderClassName")) return;
         if (unsupportedAttribute(transform, anno, "builderMethodName")) return;
         List<PropertyInfo> props;
@@ -121,8 +124,10 @@ public class ExternalStrategy extends BuilderASTTransformation.AbstractBuilderSt
         } else {
             props = getPropertyInfoFromClassNode(buildee, includes, excludes);
         }
-        for (String name : includes) {
-            checkKnownProperty(transform, anno, name, props);
+        if (includes != null) {
+            for (String name : includes) {
+                checkKnownProperty(transform, anno, name, props);
+            }
         }
         for (PropertyInfo prop : props) {
             builder.addField(createFieldCopy(builder, prop));
@@ -158,7 +163,7 @@ public class ExternalStrategy extends BuilderASTTransformation.AbstractBuilderSt
         try {
             BeanInfo beanInfo = Introspector.getBeanInfo(cNode.getTypeClass());
             for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
-                if (AbstractASTTransformation.shouldSkip(descriptor.getName(), excludes, includes)) continue;
+                if (AbstractASTTransformation.shouldSkipUndefinedAware(descriptor.getName(), excludes, includes)) continue;
                 // skip hidden and read-only props
                 if (descriptor.isHidden() || descriptor.getWriteMethod() == null) continue;
                 result.add(new PropertyInfo(descriptor.getName(), ClassHelper.make(descriptor.getPropertyType())));

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/groovy/transform/builder/InitializerStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/builder/InitializerStrategy.java b/src/main/groovy/transform/builder/InitializerStrategy.java
index a69f7f0..0f24c01 100644
--- a/src/main/groovy/transform/builder/InitializerStrategy.java
+++ b/src/main/groovy/transform/builder/InitializerStrategy.java
@@ -18,6 +18,7 @@
  */
 package groovy.transform.builder;
 
+import groovy.transform.Undefined;
 import org.codehaus.groovy.ast.AnnotatedNode;
 import org.codehaus.groovy.ast.AnnotationNode;
 import org.codehaus.groovy.ast.ClassHelper;
@@ -143,7 +144,9 @@ public class InitializerStrategy extends BuilderASTTransformation.AbstractBuilde
     private void createBuilderForAnnotatedClass(BuilderASTTransformation transform, ClassNode buildee, AnnotationNode anno, boolean useSetters) {
         List<String> excludes = new ArrayList<String>();
         List<String> includes = new ArrayList<String>();
+        includes.add(Undefined.STRING);
         if (!getIncludeExclude(transform, anno, buildee, excludes, includes)) return;
+        if (includes.size() == 1 && Undefined.isUndefined(includes.get(0))) includes = null;
         List<FieldNode> fields = getInstancePropertyFields(buildee);
         List<FieldNode> filteredFields = filterFields(fields, includes, excludes);
         if (filteredFields.isEmpty()) {
@@ -360,7 +363,7 @@ public class InitializerStrategy extends BuilderASTTransformation.AbstractBuilde
     private static List<FieldNode> filterFields(List<FieldNode> fieldNodes, List<String> includes, List<String> excludes) {
         List<FieldNode> fields = new ArrayList<FieldNode>();
         for (FieldNode fNode : fieldNodes) {
-            if (AbstractASTTransformation.shouldSkip(fNode.getName(), excludes, includes)) continue;
+            if (AbstractASTTransformation.shouldSkipUndefinedAware(fNode.getName(), excludes, includes)) continue;
             fields.add(fNode);
         }
         return fields;

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/groovy/transform/builder/SimpleStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/builder/SimpleStrategy.java b/src/main/groovy/transform/builder/SimpleStrategy.java
index 56414cd..8d54bd9 100644
--- a/src/main/groovy/transform/builder/SimpleStrategy.java
+++ b/src/main/groovy/transform/builder/SimpleStrategy.java
@@ -18,6 +18,7 @@
  */
 package groovy.transform.builder;
 
+import groovy.transform.Undefined;
 import org.codehaus.groovy.ast.AnnotatedNode;
 import org.codehaus.groovy.ast.AnnotationNode;
 import org.codehaus.groovy.ast.ClassNode;
@@ -95,15 +96,19 @@ public class SimpleStrategy extends BuilderASTTransformation.AbstractBuilderStra
 
         List<String> excludes = new ArrayList<String>();
         List<String> includes = new ArrayList<String>();
+        includes.add(Undefined.STRING);
         if (!getIncludeExclude(transform, anno, buildee, excludes, includes)) return;
+        if (includes.size() == 1 && Undefined.isUndefined(includes.get(0))) includes = null;
         String prefix = getMemberStringValue(anno, "prefix", "set");
         List<FieldNode> fields = getInstancePropertyFields(buildee);
-        for (String name : includes) {
-            checkKnownField(transform, anno, name, fields);
+        if (includes != null) {
+            for (String name : includes) {
+                checkKnownField(transform, anno, name, fields);
+            }
         }
         for (FieldNode field : fields) {
             String fieldName = field.getName();
-            if (!AbstractASTTransformation.shouldSkip(fieldName, excludes, includes)) {
+            if (!AbstractASTTransformation.shouldSkipUndefinedAware(fieldName, excludes, includes)) {
                 String methodName = getSetterName(prefix, fieldName);
                 Parameter parameter = param(field.getType(), fieldName);
                 buildee.addMethod(methodName, Opcodes.ACC_PUBLIC, newClass(buildee), params(parameter), NO_EXCEPTIONS, block(

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/org/codehaus/groovy/transform/AbstractASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/AbstractASTTransformation.java b/src/main/org/codehaus/groovy/transform/AbstractASTTransformation.java
index 0817dbd..c9be7c8 100644
--- a/src/main/org/codehaus/groovy/transform/AbstractASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/AbstractASTTransformation.java
@@ -156,17 +156,18 @@ public abstract class AbstractASTTransformation implements Opcodes, ASTTransform
     private static boolean isUndefinedMarkerList(ListExpression listExpression) {
         if (listExpression.getExpressions().size() != 1) return false;
         Expression itemExpr = listExpression.getExpression(0);
-        if (itemExpr != null && itemExpr instanceof ConstantExpression) {
+        if (itemExpr == null) return false;
+        if (itemExpr instanceof ConstantExpression) {
             Object value = ((ConstantExpression) itemExpr).getValue();
-            System.out.println("value = " + value);
-            if (isUndefined(value.toString())) return true;
+            if (value instanceof String && isUndefined((String)value)) return true;
+        } else if (itemExpr instanceof ClassExpression && isUndefined(itemExpr.getType())) {
+            return true;
         }
         return false;
     }
 
-    //@Deprecated
+    @Deprecated
     public List<String> getMemberList(AnnotationNode anno, String name) {
-        // TODO mark deprecated once all bundled AST transforms have been switched over to use getMemberStringList
         List<String> list;
         Expression expr = anno.getMember(name);
         if (expr != null && expr instanceof ListExpression) {
@@ -184,6 +185,7 @@ public abstract class AbstractASTTransformation implements Opcodes, ASTTransform
         return list;
     }
 
+    @Deprecated
     public List<ClassNode> getClassList(AnnotationNode anno, String name) {
         List<ClassNode> list = new ArrayList<ClassNode>();
         Expression expr = anno.getMember(name);
@@ -202,6 +204,31 @@ public abstract class AbstractASTTransformation implements Opcodes, ASTTransform
         return list;
     }
 
+    public List<ClassNode> getMemberClassList(AnnotationNode anno, String name) {
+        List<ClassNode> list = new ArrayList<ClassNode>();
+        Expression expr = anno.getMember(name);
+        if (expr == null) {
+            return null;
+        }
+        if (expr instanceof ListExpression) {
+            final ListExpression listExpression = (ListExpression) expr;
+            if (isUndefinedMarkerList(listExpression)) {
+                return null;
+            }
+            for (Expression itemExpr : listExpression.getExpressions()) {
+                if (itemExpr != null && itemExpr instanceof ClassExpression) {
+                    ClassNode cn = itemExpr.getType();
+                    if (cn != null) list.add(cn);
+                }
+            }
+        } else if (expr instanceof ClassExpression) {
+            ClassNode cn = expr.getType();
+            if (isUndefined(cn)) return null;
+            if (cn != null) list.add(cn);
+        }
+        return list;
+    }
+
     public void addError(String msg, ASTNode expr) {
         sourceUnit.getErrorCollector().addErrorAndContinue(new SyntaxErrorMessage(
                         new SyntaxException(msg + '\n', expr.getLineNumber(), expr.getColumnNumber(),
@@ -240,6 +267,7 @@ public abstract class AbstractASTTransformation implements Opcodes, ASTTransform
         return (excludes != null && excludes.contains(name)) || deemedInternalName(name) || (includes != null && !includes.isEmpty() && !includes.contains(name));
     }
 
+    @Deprecated
     public static boolean shouldSkipOnDescriptor(boolean checkReturn, Map genericsSpec, MethodNode mNode, List<ClassNode> excludeTypes, List<ClassNode> includeTypes) {
         String descriptor = mNode.getTypeDescriptor();
         String descriptorNoReturn = GeneralUtils.makeDescriptorWithoutReturnType(mNode);
@@ -290,7 +318,61 @@ public abstract class AbstractASTTransformation implements Opcodes, ASTTransform
         }
         return true;
     }
+    public static boolean shouldSkipOnDescriptorUndefinedAware(boolean checkReturn, Map genericsSpec, MethodNode mNode,
+                                                  List<ClassNode> excludeTypes, List<ClassNode> includeTypes) {
+        String descriptor = mNode.getTypeDescriptor();
+        String descriptorNoReturn = GeneralUtils.makeDescriptorWithoutReturnType(mNode);
+        if (excludeTypes != null) {
+            for (ClassNode cn : excludeTypes) {
+                List<ClassNode> remaining = new LinkedList<ClassNode>();
+                remaining.add(cn);
+                Map updatedGenericsSpec = new HashMap(genericsSpec);
+                while (!remaining.isEmpty()) {
+                    ClassNode next = remaining.remove(0);
+                    if (!next.equals(ClassHelper.OBJECT_TYPE)) {
+                        updatedGenericsSpec = GenericsUtils.createGenericsSpec(next, updatedGenericsSpec);
+                        for (MethodNode mn : next.getMethods()) {
+                            MethodNode correctedMethodNode = GenericsUtils.correctToGenericsSpec(updatedGenericsSpec, mn);
+                            if (checkReturn) {
+                                String md = correctedMethodNode.getTypeDescriptor();
+                                if (md.equals(descriptor)) return true;
+                            } else {
+                                String md = GeneralUtils.makeDescriptorWithoutReturnType(correctedMethodNode);
+                                if (md.equals(descriptorNoReturn)) return true;
+                            }
+                        }
+                        remaining.addAll(Arrays.asList(next.getInterfaces()));
+                    }
+                }
+            }
+        }
+        if (includeTypes == null) return false;
+        for (ClassNode cn : includeTypes) {
+            List<ClassNode> remaining = new LinkedList<ClassNode>();
+            remaining.add(cn);
+            Map updatedGenericsSpec = new HashMap(genericsSpec);
+            while (!remaining.isEmpty()) {
+                ClassNode next = remaining.remove(0);
+                if (!next.equals(ClassHelper.OBJECT_TYPE)) {
+                    updatedGenericsSpec = GenericsUtils.createGenericsSpec(next, updatedGenericsSpec);
+                    for (MethodNode mn : next.getMethods()) {
+                        MethodNode correctedMethodNode = GenericsUtils.correctToGenericsSpec(updatedGenericsSpec, mn);
+                        if (checkReturn) {
+                            String md = correctedMethodNode.getTypeDescriptor();
+                            if (md.equals(descriptor)) return false;
+                        } else {
+                            String md = GeneralUtils.makeDescriptorWithoutReturnType(correctedMethodNode);
+                            if (md.equals(descriptorNoReturn)) return false;
+                        }
+                    }
+                    remaining.addAll(Arrays.asList(next.getInterfaces()));
+                }
+            }
+        }
+        return true;
+    }
 
+    @Deprecated
     protected boolean checkIncludeExclude(AnnotationNode node, List<String> excludes, List<String> includes, String typeName) {
         if (includes != null && !includes.isEmpty() && excludes != null && !excludes.isEmpty()) {
             addError("Error during " + typeName + " processing: Only one of 'includes' and 'excludes' should be supplied not both.", node);
@@ -307,6 +389,7 @@ public abstract class AbstractASTTransformation implements Opcodes, ASTTransform
         return true;
     }
 
+    @Deprecated
     protected void checkIncludeExclude(AnnotationNode node, List<String> excludes, List<String> includes, List<ClassNode> excludeTypes, List<ClassNode> includeTypes, String typeName) {
         int found = 0;
         if (includes != null && !includes.isEmpty()) found++;
@@ -318,6 +401,18 @@ public abstract class AbstractASTTransformation implements Opcodes, ASTTransform
         }
     }
 
+    protected void checkIncludeExcludeUndefinedAware(AnnotationNode node, List<String> excludes, List<String> includes,
+                                        List<ClassNode> excludeTypes, List<ClassNode> includeTypes, String typeName) {
+        int found = 0;
+        if (includes != null) found++;
+        if (excludes != null && !excludes.isEmpty()) found++;
+        if (includeTypes != null) found++;
+        if (excludeTypes != null && !excludeTypes.isEmpty()) found++;
+        if (found > 1) {
+            addError("Error during " + typeName + " processing: Only one of 'includes', 'excludes', 'includeTypes' and 'excludeTypes' should be supplied.", node);
+        }
+    }
+
     protected boolean checkPropertyList(ClassNode cNode, List<String> propertyNameList, String listName, AnnotationNode anno, String typeName, boolean includeFields) {
         if (propertyNameList == null || propertyNameList.isEmpty()) {
             return true;

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/org/codehaus/groovy/transform/AutoCloneASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/AutoCloneASTTransformation.java b/src/main/org/codehaus/groovy/transform/AutoCloneASTTransformation.java
index 49e6535..f486a9e 100644
--- a/src/main/org/codehaus/groovy/transform/AutoCloneASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/AutoCloneASTTransformation.java
@@ -83,7 +83,7 @@ public class AutoCloneASTTransformation extends AbstractASTTransformation {
             cNode.addInterface(CLONEABLE_TYPE);
             boolean includeFields = memberHasValue(anno, "includeFields", true);
             AutoCloneStyle style = getStyle(anno, "style");
-            List<String> excludes = getMemberList(anno, "excludes");
+            List<String> excludes = getMemberStringList(anno, "excludes");
             if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields)) return;
             List<FieldNode> list = getInstancePropertyFields(cNode);
             if (includeFields) {
@@ -161,7 +161,7 @@ public class AutoCloneASTTransformation extends AbstractASTTransformation {
             }
             for (FieldNode fieldNode : list) {
                 String name = fieldNode.getName();
-                if (excludes.contains(name)) continue;
+                if (excludes != null && excludes.contains(name)) continue;
                 ClassNode fieldType = fieldNode.getType();
                 Expression direct = propX(other, name);
                 Expression to = propX(varX("this"), name);
@@ -222,7 +222,7 @@ public class AutoCloneASTTransformation extends AbstractASTTransformation {
         }
         for (FieldNode fieldNode : fieldNodes) {
             String name = fieldNode.getName();
-            if (excludes.contains(name)) continue;
+            if (excludes != null && excludes.contains(name)) continue;
             ClassNode fieldType = fieldNode.getType();
             Expression direct = propX(varX("this"), name);
             Expression to = propX(other, name);
@@ -246,7 +246,7 @@ public class AutoCloneASTTransformation extends AbstractASTTransformation {
         final Expression result = varX("_result", cNode);
         body.addStatement(declS(result, castX(cNode, callSuperX("clone"))));
         for (FieldNode fieldNode : fieldNodes) {
-            if (excludes.contains(fieldNode.getName())) continue;
+            if (excludes != null && excludes.contains(fieldNode.getName())) continue;
             ClassNode fieldType = fieldNode.getType();
             Expression fieldExpr = varX(fieldNode);
             Expression to = propX(result, fieldNode.getName());

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/org/codehaus/groovy/transform/BuilderASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/BuilderASTTransformation.java b/src/main/org/codehaus/groovy/transform/BuilderASTTransformation.java
index 7481b91..c13de3f 100644
--- a/src/main/org/codehaus/groovy/transform/BuilderASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/BuilderASTTransformation.java
@@ -129,24 +129,31 @@ public class BuilderASTTransformation extends AbstractASTTransformation implemen
         }
 
         protected boolean getIncludeExclude(BuilderASTTransformation transform, AnnotationNode anno, ClassNode cNode, List<String> excludes, List<String> includes) {
-            List<String> directExcludes = transform.getMemberList(anno, "excludes");
+            List<String> directExcludes = transform.getMemberStringList(anno, "excludes");
             if (directExcludes != null) excludes.addAll(directExcludes);
-            List<String> directIncludes = transform.getMemberList(anno, "includes");
-            if (directIncludes != null) includes.addAll(directIncludes);
-            if (includes.isEmpty() && excludes.isEmpty()) {
+            List<String> directIncludes = transform.getMemberStringList(anno, "includes");
+            if (directIncludes != null) {
+                includes.clear();
+                includes.addAll(directIncludes);
+            }
+            if (directIncludes == null && excludes.isEmpty()) {
                 if (transform.hasAnnotation(cNode, TupleConstructorASTTransformation.MY_TYPE)) {
                     AnnotationNode tupleConstructor = cNode.getAnnotations(TupleConstructorASTTransformation.MY_TYPE).get(0);
                     if (excludes.isEmpty()) {
-                        List<String>  tupleExcludes = transform.getMemberList(tupleConstructor, "excludes");
+                        List<String>  tupleExcludes = transform.getMemberStringList(tupleConstructor, "excludes");
                         if (tupleExcludes != null) excludes.addAll(tupleExcludes);
                     }
                     if (includes.isEmpty()) {
-                        List<String>  tupleIncludes = transform.getMemberList(tupleConstructor, "includes");
-                        if (tupleIncludes != null) includes.addAll(tupleIncludes);
+                        List<String>  tupleIncludes = transform.getMemberStringList(tupleConstructor, "includes");
+                        if (tupleIncludes != null) {
+                            includes.clear();
+                            includes.addAll(tupleIncludes);
+                        }
                     }
                 }
             }
-            return transform.checkIncludeExclude(anno, excludes, includes, MY_TYPE_NAME);
+            List<String> includesToCheck = includes.size() == 1 && isUndefined(includes.get(0)) ? null : includes;
+            return transform.checkIncludeExcludeUndefinedAware(anno, excludes, includesToCheck, MY_TYPE_NAME);
         }
 
         protected static class PropertyInfo {

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java b/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java
index d861002..2204671 100644
--- a/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java
@@ -116,11 +116,11 @@ public class DelegateASTTransformation extends AbstractASTTransformation {
 
             final boolean skipInterfaces = memberHasValue(node, MEMBER_INTERFACES, false);
             final boolean includeDeprecated = memberHasValue(node, MEMBER_DEPRECATED, true) || (type.isInterface() && !skipInterfaces);
-            List<String> excludes = getMemberList(node, MEMBER_EXCLUDES);
-            List<String> includes = getMemberList(node, MEMBER_INCLUDES);
-            List<ClassNode> excludeTypes = getClassList(node, MEMBER_EXCLUDE_TYPES);
-            List<ClassNode> includeTypes = getClassList(node, MEMBER_INCLUDE_TYPES);
-            checkIncludeExclude(node, excludes, includes, excludeTypes, includeTypes, MY_TYPE_NAME);
+            List<String> excludes = getMemberStringList(node, MEMBER_EXCLUDES);
+            List<String> includes = getMemberStringList(node, MEMBER_INCLUDES);
+            List<ClassNode> excludeTypes = getMemberClassList(node, MEMBER_EXCLUDE_TYPES);
+            List<ClassNode> includeTypes = getMemberClassList(node, MEMBER_INCLUDE_TYPES);
+            checkIncludeExcludeUndefinedAware(node, excludes, includes, excludeTypes, includeTypes, MY_TYPE_NAME);
 
             final List<MethodNode> ownerMethods = getAllMethods(owner);
             for (MethodNode mn : fieldMethods) {
@@ -202,10 +202,11 @@ public class DelegateASTTransformation extends AbstractASTTransformation {
         genericsSpec = addMethodGenerics(candidate, genericsSpec);
         extractSuperClassGenerics(fieldNode.getType(), candidate.getDeclaringClass(), genericsSpec);
 
-        if (!excludeTypes.isEmpty() || !includeTypes.isEmpty()) {
+        if ((excludeTypes != null && !excludeTypes.isEmpty()) || includeTypes != null) {
             MethodNode correctedMethodNode = correctToGenericsSpec(genericsSpec, candidate);
             boolean checkReturn = fieldNode.getType().getMethods().contains(candidate);
-            if (shouldSkipOnDescriptor(checkReturn, genericsSpec, correctedMethodNode, excludeTypes, includeTypes)) return;
+            if (shouldSkipOnDescriptorUndefinedAware(checkReturn, genericsSpec, correctedMethodNode, excludeTypes, includeTypes))
+                return;
         }
 
         // ignore methods from GroovyObject

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java b/src/main/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java
index 4857ebe..91e579d 100644
--- a/src/main/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java
@@ -71,9 +71,9 @@ public class EqualsAndHashCodeASTTransformation extends AbstractASTTransformatio
                 addError("Error during " + MY_TYPE_NAME + " processing: callSuper=true but '" + cNode.getName() + "' has no super class.", anno);
             }
             boolean includeFields = memberHasValue(anno, "includeFields", true);
-            List<String> excludes = getMemberList(anno, "excludes");
-            List<String> includes = getMemberList(anno, "includes");
-            if (!checkIncludeExclude(anno, excludes, includes, MY_TYPE_NAME)) return;
+            List<String> excludes = getMemberStringList(anno, "excludes");
+            List<String> includes = getMemberStringList(anno, "includes");
+            if (!checkIncludeExcludeUndefinedAware(anno, excludes, includes, MY_TYPE_NAME)) return;
             if (!checkPropertyList(cNode, includes, "includes", anno, MY_TYPE_NAME, includeFields)) return;
             if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields)) return;
             createHashCode(cNode, cacheHashCode, includeFields, callSuper, excludes, includes);

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/org/codehaus/groovy/transform/ExternalizeMethodsASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/ExternalizeMethodsASTTransformation.java b/src/main/org/codehaus/groovy/transform/ExternalizeMethodsASTTransformation.java
index 822c0bf..72ffd03 100644
--- a/src/main/org/codehaus/groovy/transform/ExternalizeMethodsASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/ExternalizeMethodsASTTransformation.java
@@ -74,7 +74,7 @@ public class ExternalizeMethodsASTTransformation extends AbstractASTTransformati
             if (!checkNotInterface(cNode, MY_TYPE_NAME)) return;
             cNode.addInterface(EXTERNALIZABLE_TYPE);
             boolean includeFields = memberHasValue(anno, "includeFields", true);
-            List<String> excludes = getMemberList(anno, "excludes");
+            List<String> excludes = getMemberStringList(anno, "excludes");
             if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields)) return;
             List<FieldNode> list = getInstancePropertyFields(cNode);
             if (includeFields) {
@@ -89,7 +89,7 @@ public class ExternalizeMethodsASTTransformation extends AbstractASTTransformati
         final BlockStatement body = new BlockStatement();
         Parameter out = param(OBJECTOUTPUT_TYPE, "out");
         for (FieldNode fNode : list) {
-            if (excludes.contains(fNode.getName())) continue;
+            if (excludes != null && excludes.contains(fNode.getName())) continue;
             if ((fNode.getModifiers() & ACC_TRANSIENT) != 0) continue;
             body.addStatement(stmt(callX(varX(out), "write" + suffixForField(fNode), varX(fNode))));
         }
@@ -101,7 +101,7 @@ public class ExternalizeMethodsASTTransformation extends AbstractASTTransformati
         final BlockStatement body = new BlockStatement();
         Parameter oin = param(OBJECTINPUT_TYPE, "oin");
         for (FieldNode fNode : list) {
-            if (excludes.contains(fNode.getName())) continue;
+            if (excludes != null && excludes.contains(fNode.getName())) continue;
             if ((fNode.getModifiers() & ACC_TRANSIENT) != 0) continue;
             String suffix = suffixForField(fNode);
             Expression readObject = callX(varX(oin), "read" + suffix);

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/org/codehaus/groovy/transform/ExternalizeVerifierASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/ExternalizeVerifierASTTransformation.java b/src/main/org/codehaus/groovy/transform/ExternalizeVerifierASTTransformation.java
index 4afeecf..70c6776 100644
--- a/src/main/org/codehaus/groovy/transform/ExternalizeVerifierASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/ExternalizeVerifierASTTransformation.java
@@ -61,7 +61,7 @@ public class ExternalizeVerifierASTTransformation extends AbstractASTTransformat
             }
             boolean includeFields = memberHasValue(anno, "includeFields", true);
             boolean checkPropertyTypes = memberHasValue(anno, "checkPropertyTypes", true);
-            List<String> excludes = getMemberList(anno, "excludes");
+            List<String> excludes = getMemberStringList(anno, "excludes");
             if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields)) return;
             List<FieldNode> list = getInstancePropertyFields(cNode);
             if (includeFields) {
@@ -73,7 +73,7 @@ public class ExternalizeVerifierASTTransformation extends AbstractASTTransformat
 
     private void checkProps(List<FieldNode> list, List<String> excludes, boolean checkPropertyTypes) {
         for (FieldNode fNode : list) {
-            if (excludes.contains(fNode.getName())) continue;
+            if (excludes != null && excludes.contains(fNode.getName())) continue;
             if ((fNode.getModifiers() & ACC_TRANSIENT) != 0) continue;
             if ((fNode.getModifiers() & ACC_FINAL) != 0) {
                 addError(MY_TYPE_NAME + ": The Externalizable property (or field) '" + fNode.getName() + "' cannot be final", fNode);

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/MapConstructorASTTransformation.java b/src/main/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
index c968a0c..0964854 100644
--- a/src/main/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
@@ -88,9 +88,9 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation {
             boolean includeProperties = !memberHasValue(anno, "includeProperties", false);
             boolean includeSuperProperties = memberHasValue(anno, "includeSuperProperties", true);
             boolean useSetters = memberHasValue(anno, "useSetters", true);
-            List<String> excludes = getMemberList(anno, "excludes");
-            List<String> includes = getMemberList(anno, "includes");
-            if (!checkIncludeExclude(anno, excludes, includes, MY_TYPE_NAME)) return;
+            List<String> excludes = getMemberStringList(anno, "excludes");
+            List<String> includes = getMemberStringList(anno, "includes");
+            if (!checkIncludeExcludeUndefinedAware(anno, excludes, includes, MY_TYPE_NAME)) return;
             if (!checkPropertyList(cNode, includes, "includes", anno, MY_TYPE_NAME, includeFields)) return;
             if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields)) return;
             // if @Immutable is found, let it pick up options and do work so we'll skip

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/org/codehaus/groovy/transform/SortableASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/SortableASTTransformation.java b/src/main/org/codehaus/groovy/transform/SortableASTTransformation.java
index a6f1eab..e054366 100644
--- a/src/main/org/codehaus/groovy/transform/SortableASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/SortableASTTransformation.java
@@ -80,9 +80,9 @@ public class SortableASTTransformation extends AbstractASTTransformation {
     }
 
     private void createSortable(AnnotationNode annotation, ClassNode classNode) {
-        List<String> includes = getMemberList(annotation, "includes");
-        List<String> excludes = getMemberList(annotation, "excludes");
-        if (!checkIncludeExclude(annotation, excludes, includes, MY_TYPE_NAME)) return;
+        List<String> includes = getMemberStringList(annotation, "includes");
+        List<String> excludes = getMemberStringList(annotation, "excludes");
+        if (!checkIncludeExcludeUndefinedAware(annotation, excludes, includes, MY_TYPE_NAME)) return;
         if (!checkPropertyList(classNode, includes, "includes", annotation, MY_TYPE_NAME, false)) return;
         if (!checkPropertyList(classNode, excludes, "excludes", annotation, MY_TYPE_NAME, false)) return;
         if (classNode.isInterface()) {
@@ -196,14 +196,14 @@ public class SortableASTTransformation extends AbstractASTTransformation {
         for (PropertyNode property : classNode.getProperties()) {
             String propertyName = property.getName();
             if (property.isStatic() ||
-                    excludes.contains(propertyName) ||
-                    !includes.isEmpty() && !includes.contains(propertyName)) continue;
+                    (excludes != null && excludes.contains(propertyName)) ||
+                    includes != null && !includes.contains(propertyName)) continue;
             properties.add(property);
         }
         for (PropertyNode pNode : properties) {
             checkComparable(pNode);
         }
-        if (!includes.isEmpty()) {
+        if (includes != null) {
             Comparator<PropertyNode> includeComparator = new Comparator<PropertyNode>() {
                 public int compare(PropertyNode o1, PropertyNode o2) {
                     return new Integer(includes.indexOf(o1.getName())).compareTo(includes.indexOf(o2.getName()));

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/main/org/codehaus/groovy/transform/ToStringASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/ToStringASTTransformation.java b/src/main/org/codehaus/groovy/transform/ToStringASTTransformation.java
index 977940f..6911ab5 100644
--- a/src/main/org/codehaus/groovy/transform/ToStringASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/ToStringASTTransformation.java
@@ -75,13 +75,13 @@ public class ToStringASTTransformation extends AbstractASTTransformation {
             }
             boolean includeNames = memberHasValue(anno, "includeNames", true);
             boolean includeFields = memberHasValue(anno, "includeFields", true);
-            List<String> excludes = getMemberList(anno, "excludes");
-            List<String> includes = getMemberList(anno, "includes");
+            List<String> excludes = getMemberStringList(anno, "excludes");
+            List<String> includes = getMemberStringList(anno, "includes");
             boolean ignoreNulls = memberHasValue(anno, "ignoreNulls", true);
             boolean includePackage = !memberHasValue(anno, "includePackage", false);
             boolean allProperties = !memberHasValue(anno, "allProperties", false);
 
-            if (!checkIncludeExclude(anno, excludes, includes, MY_TYPE_NAME)) return;
+            if (!checkIncludeExcludeUndefinedAware(anno, excludes, includes, MY_TYPE_NAME)) return;
             if (!checkPropertyList(cNode, includes, "includes", anno, MY_TYPE_NAME, includeFields)) return;
             if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields)) return;
             createToString(cNode, includeSuper, includeFields, excludes, includes, includeNames, ignoreNulls, includePackage, cacheToString, includeSuperProperties, allProperties);

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/spec/doc/core-metaprogramming.adoc
----------------------------------------------------------------------
diff --git a/src/spec/doc/core-metaprogramming.adoc b/src/spec/doc/core-metaprogramming.adoc
index 23e90ff..dc3a432 100644
--- a/src/spec/doc/core-metaprogramming.adoc
+++ b/src/spec/doc/core-metaprogramming.adoc
@@ -784,7 +784,7 @@ include::{projectdir}/src/spec/test/CodeGenerationASTTransformsTest.groovy[tags=
 ----
 include::{projectdir}/src/spec/test/CodeGenerationASTTransformsTest.groovy[tags=tostring_example_excludes,indent=0]
 ----
-|includes|Empty list|List of fields to include in toString|
+|includes|Undefined marker list (indicates all fields)|List of fields to include in toString|
 [source,groovy]
 ----
 include::{projectdir}/src/spec/test/CodeGenerationASTTransformsTest.groovy[tags=tostring_example_includes,indent=0]
@@ -848,7 +848,7 @@ There are several options available to tweak the behavior of `@EqualsAndHashCode
 ----
 include::{projectdir}/src/spec/test/CodeGenerationASTTransformsTest.groovy[tags=equalshashcode_example_excludes,indent=0]
 ----
-|includes|Empty list|List of fields to include in equals/hashCode|
+|includes|Undefined marker list (indicating all fields)|List of fields to include in equals/hashCode|
 [source,groovy]
 ----
 include::{projectdir}/src/spec/test/CodeGenerationASTTransformsTest.groovy[tags=equalshashcode_example_includes,indent=0]
@@ -966,6 +966,25 @@ include::{projectdir}/src/spec/test/CodeGenerationASTTransformsTest.groovy[tags=
 ----
 |=======================================================================
 
+[[xform-MapConstructor]]
+===== @groovy.transform.MapConstructor
+
+The `@MapConstructor` annotation aims at eliminating boilerplate code by generating a map constructor for you. A map
+constructor is created such that each property in the class is set based on the value in the supplied map
+having the key with the name of the property. Usage is as shown in this example:
+
+[source,groovy]
+----
+include::{projectdir}/src/spec/test/CodeGenerationASTTransformsTest.groovy[tags=mapconstructor_simple,indent=0]
+----
+
+The generated constructor will be roughly like this:
+
+[source,groovy]
+----
+include::{projectdir}/src/spec/test/CodeGenerationASTTransformsTest.groovy[tags=mapconstructor_equiv,indent=0]
+----
+
 [[xform-Canonical]]
 ===== @groovy.transform.Canonical
 
@@ -1232,9 +1251,10 @@ And calling the chained setters would look like this:
 include::{projectdir}/src/spec/test/CodeGenerationASTTransformsTest.groovy[tags=builder_simple_prefix_usage,indent=0]
 ----
 
-You can use the `SimpleStrategy` in conjunction with `@Canonical`. If your `@Builder` annotation doesn't have
-explicit `includes` or `excludes` annotation attributes but your `@Canonical` annotation does, the ones
-from `@Canonical` will be re-used for `@Builder`.
+You can use the `SimpleStrategy` in conjunction with `@TupleConstructor`. If your `@Builder`
+annotation doesn't have explicit `includes` or `excludes` annotation attributes but your `@TupleConstructor`
+annotation does, the ones from `@TupleConstructor` will be re-used for `@Builder`. The same applies for any
+annotation aliases which combine `@TupleConstructor` such as `@Canonical`.
 
 The annotation attribute `useSetters` can be used if you have a setter which you want called as part of the
 construction process. See the JavaDoc for details.
@@ -1290,9 +1310,10 @@ include::{projectdir}/src/spec/test/CodeGenerationASTTransformsTest.groovy[tags=
 
 The `builderMethodName` and `builderClassName` annotation attributes for `@Builder` aren't applicable for this strategy.
 
-You can use the `ExternalStrategy` in conjunction with `@Canonical`. If your `@Builder` annotation doesn't have
-explicit `includes` or `excludes` annotation attributes but the `@Canonical` annotation of the class you are creating
-the builder for does, the ones from `@Canonical` will be re-used for `@Builder`.
+You can use the `ExternalStrategy` in conjunction with `@TupleConstructor`. If your `@Builder` annotation doesn't have
+explicit `includes` or `excludes` annotation attributes but the `@TupleConstructor` annotation of the class you are creating
+the builder for does, the ones from `@TupleConstructor` will be re-used for `@Builder`. The same applies for any
+annotation aliases which combine `@TupleConstructor` such as `@Canonical`.
 
 .DefaultStrategy
 To use the `DefaultStrategy`, annotate your Groovy class using the `@Builder` annotation as shown in this example:
@@ -1428,7 +1449,9 @@ include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=del
 include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=delegate_example_excludes_header,indent=0]
 include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=delegate_example_excludes_footer,indent=0]
 ----
-|includes|Empty array|A list of methods to be included in delegation. For more fine-grained control, see also `includeTypes`.|
+|includes|Undefined marker array (indicates all methods)|A list of methods to be included in delegation. For more
+fine-grained
+control, see also `includeTypes`.|
 [source,groovy]
 ----
 include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=delegate_example_includes_header,indent=0]
@@ -1439,7 +1462,8 @@ include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=del
 ----
 include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=delegate_example_excludeTypes,indent=0]
 ----
-|includeTypes|Empty array|A list of interfaces containing method signatures to be included in delegation|
+|includeTypes|Undefined marker array (indicates no list be default)|A list of interfaces containing method signatures to
+ be included in delegation|
 [source,groovy]
 ----
 include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=delegate_example_includeTypes_header,indent=0]

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/b6ff4efe/src/spec/test/CodeGenerationASTTransformsTest.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/CodeGenerationASTTransformsTest.groovy b/src/spec/test/CodeGenerationASTTransformsTest.groovy
index a8d27ef..381111b 100644
--- a/src/spec/test/CodeGenerationASTTransformsTest.groovy
+++ b/src/spec/test/CodeGenerationASTTransformsTest.groovy
@@ -581,6 +581,36 @@ assert Musician.constructors.size() == 1
 '''
     }
 
+    void testMapConstructor() {
+        assertScript '''
+// tag::mapconstructor_simple[]
+import groovy.transform.*
+
+@ToString
+@MapConstructor
+class Person {
+    String firstName
+    String lastName
+}
+
+def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')
+assert p1.toString() == 'Person(Jack, Nicholson)'
+// end::mapconstructor_simple[]
+        '''
+/*
+// tag::mapconstructor_equiv[]
+public Person(Map args) {
+    if (args.containsKey('firstName')) {
+        this.firstName = args.get('firstName')
+    }
+    if (args.containsKey('lastName')) {
+        this.lastName = args.get('lastName')
+    }
+}
+// end::mapconstructor_equiv[]
+*/
+    }
+
     void testCanonical() {
         assertScript '''
 // tag::canonical_simple[]