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) {

Reply via email to