Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 6DCB3200D0E for ; Tue, 12 Sep 2017 08:13:29 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 6C5331609C5; Tue, 12 Sep 2017 06:13:29 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 8A96F1609C4 for ; Tue, 12 Sep 2017 08:13:28 +0200 (CEST) Received: (qmail 97046 invoked by uid 500); 12 Sep 2017 06:13:27 -0000 Mailing-List: contact commits-help@groovy.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@groovy.apache.org Delivered-To: mailing list commits@groovy.apache.org Received: (qmail 97037 invoked by uid 99); 12 Sep 2017 06:13:27 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 12 Sep 2017 06:13:27 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 91271F57B4; Tue, 12 Sep 2017 06:13:27 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: paulk@apache.org To: commits@groovy.apache.org Message-Id: <0ddd0344cf794437a6c3e9fa124c7d98@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: groovy git commit: GROOVY-8300: Initial version having no configuration options (closes #594) Date: Tue, 12 Sep 2017 06:13:27 +0000 (UTC) archived-at: Tue, 12 Sep 2017 06:13:29 -0000 Repository: groovy Updated Branches: refs/heads/GROOVY_2_5_X 61c460b17 -> 195a95670 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/195a9567 Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/195a9567 Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/195a9567 Branch: refs/heads/GROOVY_2_5_X Commit: 195a956703ced1bd369bc1dfa5b213c8b4cb2778 Parents: 61c460b Author: paulk Authored: Tue Aug 29 10:57:53 2017 +1000 Committer: paulk Committed: Tue Sep 12 16:13:15 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/195a9567/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. + *

+ * Example usage: + *

+ * {@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'
+ * 
+ * for this case, the constructor for the Person class will be + * equivalent to the following code: + *
+ * Person(final String first, final String last) {
+ *     //...
+ * }
+ * 
+ * And after normal default parameter processing takes place, the following overloaded methods will exist: + *
+ * String fullName(final boolean reversed, final String separator) { ... }
+ * String fullName(final boolean reversed) { fullName(reversed, ' ') }
+ * String fullName() { fullName(false) }
+ * 
+ * + * @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/195a9567/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/195a9567/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' + ''' + } +}