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 2017/09/12 06:12:47 UTC

groovy git commit: GROOVY-8300: Initial version having no configuration options (closes #594)

Repository: groovy
Updated Branches:
  refs/heads/GROOVY_2_6_X af53806c8 -> b11778c22


GROOVY-8300: Initial version having no configuration options (closes #594)


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

Branch: refs/heads/GROOVY_2_6_X
Commit: b11778c2212684e5e52e20addd0a48bbf03f59fb
Parents: af53806
Author: paulk <pa...@asert.com.au>
Authored: Tue Aug 29 10:57:53 2017 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Tue Sep 12 16:12:35 2017 +1000

----------------------------------------------------------------------
 src/main/groovy/transform/AutoFinal.java        | 75 ++++++++++++++++++
 .../transform/AutoFinalASTTransformation.java   | 81 ++++++++++++++++++++
 .../transform/AutoFinalTransformTest.groovy     | 64 ++++++++++++++++
 3 files changed, 220 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/b11778c2/src/main/groovy/transform/AutoFinal.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/AutoFinal.java b/src/main/groovy/transform/AutoFinal.java
new file mode 100644
index 0000000..5fb673f
--- /dev/null
+++ b/src/main/groovy/transform/AutoFinal.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.transform;
+
+import org.codehaus.groovy.transform.GroovyASTTransformationClass;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to automatically add final to various syntactic structures,
+ * saving you typing of some boilerplate code.
+ * Initially, only method and constructor parameters are supported.
+ * The annotation may be placed on any method or constructor.
+ * It can also be placed at the class level in which case it applies to
+ * all methods and constructors within the class.
+ * <p>
+ * <em>Example usage:</em>
+ * <pre class="groovyTestCase">
+ * {@code @groovy.transform.AutoFinal}
+ * class Person {
+ *     final String first, last
+ *     Person(String first, String last) {
+ *         this.first = first
+ *         this.last = last
+ *     }
+ *     String fullName(boolean reversed = false, String separator = ' ') {
+ *         "${reversed ? last : first}$separator${reversed ? first : last}"
+ *     }
+ * }
+ *
+ * def js = new Person('John', 'Smith')
+ * assert js.fullName() == 'John Smith'
+ * assert js.fullName(true, ', ') == 'Smith, John'
+ * </pre>
+ * for this case, the constructor for the <code>Person</code> class will be
+ * equivalent to the following code:
+ * <pre>
+ * Person(final String first, final String last) {
+ *     //...
+ * }
+ * </pre>
+ * And after normal default parameter processing takes place, the following overloaded methods will exist:
+ * <pre>
+ * String fullName(final boolean reversed, final String separator) { ... }
+ * String fullName(final boolean reversed) { fullName(reversed, ' ') }
+ * String fullName() { fullName(false) }
+ * </pre>
+ *
+ * @since 2.5.0
+ */
+@java.lang.annotation.Documented
+@Retention(RetentionPolicy.SOURCE)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
+@GroovyASTTransformationClass("org.codehaus.groovy.transform.AutoFinalASTTransformation")
+public @interface AutoFinal {
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b11778c2/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java b/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
new file mode 100644
index 0000000..36fc7b1
--- /dev/null
+++ b/src/main/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
@@ -0,0 +1,81 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.transform;
+
+import groovy.transform.AutoFinal;
+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.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.lang.reflect.Modifier;
+
+import static org.codehaus.groovy.ast.ClassHelper.make;
+
+/**
+ * Handles generation of code for the {@code @}AutoFinal annotation.
+ */
+@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
+public class AutoFinalASTTransformation extends AbstractASTTransformation {
+
+    private static final Class MY_CLASS = AutoFinal.class;
+    private static final ClassNode MY_TYPE = make(MY_CLASS);
+    private static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage();
+
+    public void visit(ASTNode[] nodes, SourceUnit source) {
+        init(nodes, source);
+        AnnotatedNode candidate = (AnnotatedNode) nodes[1];
+        AnnotationNode node = (AnnotationNode) nodes[0];
+        if (!MY_TYPE.equals(node.getClassNode())) return;
+
+        if (candidate instanceof ClassNode) {
+            processClass((ClassNode) candidate);
+        } else if (candidate instanceof MethodNode) {
+            // handles constructors and methods
+            processConstructorOrMethod((MethodNode) candidate);
+        }
+    }
+
+    private void processClass(ClassNode cNode) {
+        if (cNode.isInterface()) {
+            addError("Error processing interface '" + cNode.getName() +
+                    "'. " + MY_TYPE_NAME + " only allowed for classes.", cNode);
+            return;
+        }
+        for (ConstructorNode cn : cNode.getDeclaredConstructors()) {
+            processConstructorOrMethod(cn);
+        }
+        for (MethodNode mn : cNode.getAllDeclaredMethods()) {
+            processConstructorOrMethod(mn);
+        }
+    }
+
+    private void processConstructorOrMethod(MethodNode node) {
+        if (node.isSynthetic()) return;
+        Parameter[] origParams = node.getParameters();
+        for (Parameter p : origParams) {
+            p.setModifiers(p.getModifiers() | Modifier.FINAL);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b11778c2/src/test/org/codehaus/groovy/transform/AutoFinalTransformTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/org/codehaus/groovy/transform/AutoFinalTransformTest.groovy b/src/test/org/codehaus/groovy/transform/AutoFinalTransformTest.groovy
new file mode 100644
index 0000000..339daa4
--- /dev/null
+++ b/src/test/org/codehaus/groovy/transform/AutoFinalTransformTest.groovy
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.transform
+
+import gls.CompilableTestSupport
+
+/**
+ * Tests for the {@code @AutoFinal} AST transform.
+ */
+class AutoFinalTransformTest extends CompilableTestSupport {
+
+    void testAutoFinalOnClass() {
+        // use ASTTest here since final modifier isn't put into bytecode so not available via reflection
+        assertScript '''
+            import groovy.transform.AutoFinal
+            import groovy.transform.ASTTest
+            import static org.codehaus.groovy.control.CompilePhase.SEMANTIC_ANALYSIS
+            import static java.lang.reflect.Modifier.isFinal
+
+            @ASTTest(phase=SEMANTIC_ANALYSIS, value = {
+                assert node.methods.size() == 1
+                node.methods[0].with {
+                    assert it.name == 'fullName'
+                    assert it.parameters.every{ p -> isFinal(p.modifiers) }
+                }
+                assert node.constructors.size() == 1
+                node.constructors[0].with {
+                    assert it.parameters.every{ p -> isFinal(p.modifiers) }
+                }
+            })
+            @AutoFinal
+            class Person {
+                final String first, last
+                Person(String first, String last) {
+                    this.first = first
+                    this.last = last
+                }
+                String fullName(boolean reversed = false, String separator = ' ') {
+                    "${reversed ? last : first}$separator${reversed ? first : last}"
+                }
+            }
+
+            def js = new Person('John', 'Smith')
+            assert js.fullName() == 'John Smith'
+            assert js.fullName(true, ', ') == 'Smith, John'
+        '''
+    }
+}