GROOVY-8456: @MapConstructor could provide a noArg flag
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/09e4a10d Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/09e4a10d Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/09e4a10d Branch: refs/heads/GROOVY_2_5_X Commit: 09e4a10dea91609d144fee3751e2a743889324f0 Parents: 02008fb Author: paulk <[email protected]> Authored: Tue Jan 23 11:32:41 2018 +1000 Committer: paulk <[email protected]> Committed: Tue Jan 23 11:39:42 2018 +1000 ---------------------------------------------------------------------- .../groovy/groovy/transform/MapConstructor.java | 15 ++++++++++----- .../apache/groovy/ast/tools/ClassNodeUtils.java | 11 +++++++++++ .../ExternalizeVerifierASTTransformation.java | 18 ++++-------------- .../MapConstructorASTTransformation.java | 17 +++++++++++++++-- 4 files changed, 40 insertions(+), 21 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/09e4a10d/src/main/groovy/groovy/transform/MapConstructor.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/MapConstructor.java b/src/main/groovy/groovy/transform/MapConstructor.java index 5608001..64cd1da 100644 --- a/src/main/groovy/groovy/transform/MapConstructor.java +++ b/src/main/groovy/groovy/transform/MapConstructor.java @@ -112,6 +112,16 @@ public @interface MapConstructor { boolean useSetters() default false; /** + * Whether to include all fields and/or properties within the constructor, including those with names that are considered internal. + */ + boolean allNames() default false; + + /** + * In addition to the map constructor, provide a no-arg constructor which calls the map constructor with an empty map. + */ + boolean noArg() default false; + + /** * A Closure containing statements which will be prepended to the generated constructor. The first statement within the Closure may be "super(someArgs)" in which case the no-arg super constructor won't be called. */ Class pre() default Undefined.CLASS.class; @@ -120,9 +130,4 @@ public @interface MapConstructor { * A Closure containing statements which will be appended to the end of the generated constructor. Useful for validation steps or tweaking the populated fields/properties. */ Class post() default Undefined.CLASS.class; - - /** - * Whether to include all fields and/or properties within the constructor, including those with names that are considered internal. - */ - boolean allNames() default false; } http://git-wip-us.apache.org/repos/asf/groovy/blob/09e4a10d/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java b/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java index 98e0fe2..6d28afc 100644 --- a/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java +++ b/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java @@ -20,6 +20,7 @@ package org.apache.groovy.ast.tools; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ConstructorNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.PropertyNode; @@ -270,4 +271,14 @@ public class ClassNodeUtils { } private ClassNodeUtils() { } + + public static boolean hasNoArgConstructor(ClassNode cNode) { + List<ConstructorNode> constructors = cNode.getDeclaredConstructors(); + for (ConstructorNode next : constructors) { + if (next.getParameters().length == 0) { + return true; + } + } + return false; + } } http://git-wip-us.apache.org/repos/asf/groovy/blob/09e4a10d/src/main/java/org/codehaus/groovy/transform/ExternalizeVerifierASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/transform/ExternalizeVerifierASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/ExternalizeVerifierASTTransformation.java index 23b8689..602d84a 100644 --- a/src/main/java/org/codehaus/groovy/transform/ExternalizeVerifierASTTransformation.java +++ b/src/main/java/org/codehaus/groovy/transform/ExternalizeVerifierASTTransformation.java @@ -23,7 +23,6 @@ import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.AnnotatedNode; import org.codehaus.groovy.ast.AnnotationNode; import org.codehaus.groovy.ast.ClassNode; -import org.codehaus.groovy.ast.ConstructorNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.control.CompilePhase; import org.codehaus.groovy.control.SourceUnit; @@ -32,13 +31,14 @@ import java.io.Externalizable; import java.io.Serializable; import java.util.List; +import static org.apache.groovy.ast.tools.ClassNodeUtils.hasNoArgConstructor; import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType; import static org.codehaus.groovy.ast.ClassHelper.make; import static org.codehaus.groovy.ast.tools.GeneralUtils.getInstanceNonPropertyFields; import static org.codehaus.groovy.ast.tools.GeneralUtils.getInstancePropertyFields; -@GroovyASTTransformation(phase = CompilePhase.CLASS_GENERATION) -public class ExternalizeVerifierASTTransformation extends AbstractASTTransformation { [email protected](phase = CompilePhase.CLASS_GENERATION) +public class ExternalizeVerifierASTTransformation extends org.codehaus.groovy.transform.AbstractASTTransformation { static final Class MY_CLASS = ExternalizeVerifier.class; static final ClassNode MY_TYPE = make(MY_CLASS); static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage(); @@ -53,7 +53,7 @@ public class ExternalizeVerifierASTTransformation extends AbstractASTTransformat if (parent instanceof ClassNode) { ClassNode cNode = (ClassNode) parent; - if (!hasNoargConstructor(cNode)) { + if (!hasNoArgConstructor(cNode)) { addError(MY_TYPE_NAME + ": An Externalizable class requires a no-arg constructor but none found", cNode); } if (!implementsExternalizable(cNode)) { @@ -94,14 +94,4 @@ public class ExternalizeVerifierASTTransformation extends AbstractASTTransformat return cNode.implementsInterface(SERIALIZABLE_TYPE); } - private static boolean hasNoargConstructor(ClassNode cNode) { - List<ConstructorNode> constructors = cNode.getDeclaredConstructors(); - for (ConstructorNode next : constructors) { - if (next.getParameters().length == 0) { - return true; - } - } - return false; - } - } http://git-wip-us.apache.org/repos/asf/groovy/blob/09e4a10d/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java index 723800f..a1ad411 100644 --- a/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java +++ b/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java @@ -31,9 +31,11 @@ import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.expr.ArgumentListExpression; import org.codehaus.groovy.ast.expr.ClosureExpression; import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.MapExpression; import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.ast.stmt.BlockStatement; import org.codehaus.groovy.ast.stmt.EmptyStatement; +import org.codehaus.groovy.ast.stmt.Statement; import org.codehaus.groovy.control.CompilePhase; import org.codehaus.groovy.control.SourceUnit; @@ -41,6 +43,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import static org.apache.groovy.ast.tools.ClassNodeUtils.hasNoArgConstructor; import static org.codehaus.groovy.ast.ClassHelper.make; import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching; import static org.codehaus.groovy.ast.tools.GeneralUtils.args; @@ -49,6 +52,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX; import static org.codehaus.groovy.ast.tools.GeneralUtils.callX; import static org.codehaus.groovy.ast.tools.GeneralUtils.constX; import static org.codehaus.groovy.ast.tools.GeneralUtils.copyStatementsWithSuperAdjustment; +import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX; import static org.codehaus.groovy.ast.tools.GeneralUtils.getInstanceNonPropertyFields; import static org.codehaus.groovy.ast.tools.GeneralUtils.getInstancePropertyFields; import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName; @@ -86,6 +90,7 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation { boolean includeProperties = !memberHasValue(anno, "includeProperties", false); boolean includeSuperProperties = memberHasValue(anno, "includeSuperProperties", true); boolean useSetters = memberHasValue(anno, "useSetters", true); + boolean noArg = memberHasValue(anno, "noArg", true); List<String> excludes = getMemberStringList(anno, "excludes"); List<String> includes = getMemberStringList(anno, "includes"); boolean allNames = memberHasValue(anno, "allNames", true); @@ -106,7 +111,7 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation { return; } - createConstructor(cNode, includeFields, includeProperties, includeSuperProperties, useSetters, excludes, includes, (ClosureExpression) pre, (ClosureExpression) post, source, allNames); + createConstructors(cNode, includeFields, includeProperties, includeSuperProperties, useSetters, noArg, allNames, excludes, includes, (ClosureExpression) pre, (ClosureExpression) post, source); if (pre != null) { anno.setMember("pre", new ClosureExpression(new Parameter[0], new EmptyStatement())); } @@ -116,7 +121,7 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation { } } - public static void createConstructor(ClassNode cNode, boolean includeFields, boolean includeProperties, boolean includeSuperProperties, boolean useSetters, List<String> excludes, List<String> includes, ClosureExpression pre, ClosureExpression post, SourceUnit source, boolean allNames) { + public static void createConstructors(ClassNode cNode, boolean includeFields, boolean includeProperties, boolean includeSuperProperties, boolean useSetters, boolean noArg, boolean allNames, List<String> excludes, List<String> includes, ClosureExpression pre, ClosureExpression post, SourceUnit source) { List<ConstructorNode> constructors = cNode.getDeclaredConstructors(); boolean foundEmpty = constructors.size() == 1 && constructors.get(0).getFirstStatement() == null; // HACK: JavaStubGenerator could have snuck in a constructor we don't want @@ -159,6 +164,14 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation { body.addStatement(transformed.getCode()); } cNode.addConstructor(new ConstructorNode(ACC_PUBLIC, params(map), ClassNode.EMPTY_ARRAY, body)); + if (noArg && !(list.isEmpty() && superList.isEmpty()) && !hasNoArgConstructor(cNode)) { + createNoArgConstructor(cNode); + } + } + + private static void createNoArgConstructor(ClassNode cNode) { + Statement body = stmt(ctorX(ClassNode.THIS, args(new MapExpression()))); + cNode.addConstructor(new ConstructorNode(ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body)); } private static void assignField(boolean useSetters, Parameter map, BlockStatement body, String name) {
