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[]