http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/TailRecursiveASTTransformation.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/TailRecursiveASTTransformation.groovy b/src/main/groovy/TailRecursiveASTTransformation.groovy deleted file mode 100644 index 0605f18..0000000 --- a/src/main/groovy/TailRecursiveASTTransformation.groovy +++ /dev/null @@ -1,261 +0,0 @@ -/* - * 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.tailrec - -import groovy.transform.CompileStatic -import groovy.transform.Memoized -import groovy.transform.TailRecursive -import org.codehaus.groovy.ast.ASTNode -import org.codehaus.groovy.ast.AnnotationNode -import org.codehaus.groovy.ast.ClassHelper -import org.codehaus.groovy.ast.ClassNode -import org.codehaus.groovy.ast.MethodNode -import org.codehaus.groovy.ast.Parameter -import org.codehaus.groovy.ast.expr.Expression -import org.codehaus.groovy.ast.expr.MethodCallExpression -import org.codehaus.groovy.ast.expr.StaticMethodCallExpression -import org.codehaus.groovy.ast.expr.TernaryExpression -import org.codehaus.groovy.ast.expr.VariableExpression -import org.codehaus.groovy.ast.stmt.BlockStatement -import org.codehaus.groovy.ast.stmt.ReturnStatement -import org.codehaus.groovy.ast.stmt.Statement -import org.codehaus.groovy.classgen.ReturnAdder -import org.codehaus.groovy.classgen.VariableScopeVisitor -import org.codehaus.groovy.control.CompilePhase -import org.codehaus.groovy.control.SourceUnit -import org.codehaus.groovy.transform.AbstractASTTransformation -import org.codehaus.groovy.transform.GroovyASTTransformation - -/** - * Handles generation of code for the @TailRecursive annotation. - * - * It's doing its work in the earliest possible compile phase - * - * @author Johannes Link - */ -@CompileStatic -@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) -class TailRecursiveASTTransformation extends AbstractASTTransformation { - - private static final Class MY_CLASS = TailRecursive.class; - private static final ClassNode MY_TYPE = new ClassNode(MY_CLASS); - static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage() - private HasRecursiveCalls hasRecursiveCalls = new HasRecursiveCalls() - private TernaryToIfStatementConverter ternaryToIfStatement = new TernaryToIfStatementConverter() - - - @Override - public void visit(ASTNode[] nodes, SourceUnit source) { - init(nodes, source); - - MethodNode method = nodes[1] as MethodNode - - if (method.isAbstract()) { - addError("Annotation " + MY_TYPE_NAME + " cannot be used for abstract methods.", method); - return; - } - - if (hasAnnotation(method, ClassHelper.make(Memoized))) { - ClassNode memoizedClassNode = ClassHelper.make(Memoized) - for (AnnotationNode annotationNode in method.annotations) { - if (annotationNode.classNode == MY_TYPE) - break - if (annotationNode.classNode == memoizedClassNode) { - addError("Annotation " + MY_TYPE_NAME + " must be placed before annotation @Memoized.", annotationNode) - return - } - } - } - - if (!hasRecursiveMethodCalls(method)) { - AnnotationNode annotationNode = method.getAnnotations(ClassHelper.make(TailRecursive))[0] - addError("No recursive calls detected. You must remove annotation " + MY_TYPE_NAME + ".", annotationNode) - return; - } - - transformToIteration(method, source) - ensureAllRecursiveCallsHaveBeenTransformed(method) - } - - private boolean hasAnnotation(MethodNode methodNode, ClassNode annotation) { - List annots = methodNode.getAnnotations(annotation); - return (annots != null && annots.size() > 0); - } - - - private void transformToIteration(MethodNode method, SourceUnit source) { - if (method.isVoidMethod()) { - transformVoidMethodToIteration(method, source) - } else { - transformNonVoidMethodToIteration(method, source) - } - } - - private void transformVoidMethodToIteration(MethodNode method, SourceUnit source) { - addError("Void methods are not supported by @TailRecursive yet.", method) - } - - private void transformNonVoidMethodToIteration(MethodNode method, SourceUnit source) { - addMissingDefaultReturnStatement(method) - replaceReturnsWithTernariesToIfStatements(method) - wrapMethodBodyWithWhileLoop(method) - - Map<String, Map> nameAndTypeMapping = name2VariableMappingFor(method) - replaceAllAccessToParams(method, nameAndTypeMapping) - addLocalVariablesForAllParameters(method, nameAndTypeMapping) //must happen after replacing access to params - - Map<Integer, Map> positionMapping = position2VariableMappingFor(method) - replaceAllRecursiveReturnsWithIteration(method, positionMapping) - repairVariableScopes(source, method) - } - - private void repairVariableScopes(SourceUnit source, MethodNode method) { - new VariableScopeVisitor(source).visitClass(method.declaringClass) - } - - private void replaceReturnsWithTernariesToIfStatements(MethodNode method) { - Closure<Boolean> whenReturnWithTernary = { ASTNode node -> - if (!(node instanceof ReturnStatement)) { - return false - } - return (((ReturnStatement) node).expression instanceof TernaryExpression) - } - Closure<Statement> replaceWithIfStatement = { ReturnStatement statement -> - ternaryToIfStatement.convert(statement) - } - StatementReplacer replacer = new StatementReplacer(when: whenReturnWithTernary, replaceWith: replaceWithIfStatement) - replacer.replaceIn(method.code) - - } - - private void addLocalVariablesForAllParameters(MethodNode method, Map<String, Map> nameAndTypeMapping) { - BlockStatement code = method.code as BlockStatement - nameAndTypeMapping.each { String paramName, Map localNameAndType -> - code.statements.add(0, AstHelper.createVariableDefinition( - (String) localNameAndType['name'], - (ClassNode) localNameAndType['type'], - new VariableExpression(paramName, (ClassNode) localNameAndType['type']) - )) - } - } - - private void replaceAllAccessToParams(MethodNode method, Map<String, Map> nameAndTypeMapping) { - new VariableAccessReplacer(nameAndTypeMapping: nameAndTypeMapping).replaceIn(method.code) - } - - // Public b/c there are tests for this method - Map<String, Map> name2VariableMappingFor(MethodNode method) { - Map<String, Map> nameAndTypeMapping = [:] - method.parameters.each { Parameter param -> - String paramName = param.name - ClassNode paramType = param.type as ClassNode - String iterationVariableName = iterationVariableName(paramName) - nameAndTypeMapping[paramName] = [name: iterationVariableName, type: paramType] - } - return nameAndTypeMapping - } - - // Public b/c there are tests for this method - Map<Integer, Map> position2VariableMappingFor(MethodNode method) { - Map<Integer, Map> positionMapping = [:] - method.parameters.eachWithIndex { Parameter param, int index -> - String paramName = param.name - ClassNode paramType = param.type as ClassNode - String iterationVariableName = this.iterationVariableName(paramName) - positionMapping[index] = [name: iterationVariableName, type: paramType] - } - return positionMapping - } - - private String iterationVariableName(String paramName) { - '_' + paramName + '_' - } - - private void replaceAllRecursiveReturnsWithIteration(MethodNode method, Map positionMapping) { - replaceRecursiveReturnsOutsideClosures(method, positionMapping) - replaceRecursiveReturnsInsideClosures(method, positionMapping) - } - - private void replaceRecursiveReturnsOutsideClosures(MethodNode method, Map<Integer, Map> positionMapping) { - Closure<Boolean> whenRecursiveReturn = { Statement statement, boolean inClosure -> - if (inClosure) - return false - if (!(statement instanceof ReturnStatement)) { - return false - } - Expression inner = ((ReturnStatement) statement).expression - if (!(inner instanceof MethodCallExpression) && !(inner instanceof StaticMethodCallExpression)) { - return false - } - return isRecursiveIn(inner, method) - } - Closure<Statement> replaceWithContinueBlock = { ReturnStatement statement -> - new ReturnStatementToIterationConverter().convert(statement, positionMapping) - } - def replacer = new StatementReplacer(when: whenRecursiveReturn, replaceWith: replaceWithContinueBlock) - replacer.replaceIn(method.code) - } - - private void replaceRecursiveReturnsInsideClosures(MethodNode method, Map<Integer, Map> positionMapping) { - Closure<Boolean> whenRecursiveReturn = { Statement statement, boolean inClosure -> - if (!inClosure) - return false - if (!(statement instanceof ReturnStatement)) { - return false - } - Expression inner = ((ReturnStatement )statement).expression - if (!(inner instanceof MethodCallExpression) && !(inner instanceof StaticMethodCallExpression)) { - return false - } - return isRecursiveIn(inner, method) - } - Closure<Statement> replaceWithThrowLoopException = { ReturnStatement statement -> - new ReturnStatementToIterationConverter(recurStatement: AstHelper.recurByThrowStatement()).convert(statement, positionMapping) - } - StatementReplacer replacer = new StatementReplacer(when: whenRecursiveReturn, replaceWith: replaceWithThrowLoopException) - replacer.replaceIn(method.code) - } - - private void wrapMethodBodyWithWhileLoop(MethodNode method) { - new InWhileLoopWrapper().wrap(method) - } - - private void addMissingDefaultReturnStatement(MethodNode method) { - new ReturnAdder().visitMethod(method) - new ReturnAdderForClosures().visitMethod(method) - } - - private void ensureAllRecursiveCallsHaveBeenTransformed(MethodNode method) { - List<Expression> remainingRecursiveCalls = new CollectRecursiveCalls().collect(method) - for(Expression expression : remainingRecursiveCalls) { - addError("Recursive call could not be transformed by @TailRecursive. Maybe it's not a tail call.", expression) - } - } - - private boolean hasRecursiveMethodCalls(MethodNode method) { - hasRecursiveCalls.test(method) - } - - private boolean isRecursiveIn(Expression methodCall, MethodNode method) { - if (methodCall instanceof MethodCallExpression) - return new RecursivenessTester().isRecursive(method, (MethodCallExpression) methodCall) - if (methodCall instanceof StaticMethodCallExpression) - return new RecursivenessTester().isRecursive(method, (StaticMethodCallExpression) methodCall) - } -} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/TernaryToIfStatementConverter.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/TernaryToIfStatementConverter.groovy b/src/main/groovy/TernaryToIfStatementConverter.groovy deleted file mode 100644 index c47e1d2..0000000 --- a/src/main/groovy/TernaryToIfStatementConverter.groovy +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.tailrec - -import groovy.transform.CompileStatic -import org.codehaus.groovy.ast.expr.TernaryExpression -import org.codehaus.groovy.ast.stmt.IfStatement -import org.codehaus.groovy.ast.stmt.ReturnStatement -import org.codehaus.groovy.ast.stmt.Statement - -/** - * Since a ternary statement has more than one exit point tail-recursiveness testing cannot be easily done. - * Therefore this class translates a ternary statement (or Elvis operator) into the equivalent if-else statement. - * - * @author Johannes Link - */ -@CompileStatic -class TernaryToIfStatementConverter { - - Statement convert(ReturnStatement statementWithInnerTernaryExpression) { - if (!(statementWithInnerTernaryExpression.expression instanceof TernaryExpression)) - return statementWithInnerTernaryExpression - TernaryExpression ternary = statementWithInnerTernaryExpression.expression as TernaryExpression - return new IfStatement(ternary.booleanExpression, new ReturnStatement(ternary.trueExpression), new ReturnStatement(ternary.falseExpression)) - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/TransformTestHelper.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/TransformTestHelper.groovy b/src/main/groovy/TransformTestHelper.groovy deleted file mode 100644 index d9921d5..0000000 --- a/src/main/groovy/TransformTestHelper.groovy +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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.tools.ast - -import org.codehaus.groovy.ast.ClassNode -import org.codehaus.groovy.classgen.GeneratorContext -import org.codehaus.groovy.control.CompilationUnit -import org.codehaus.groovy.control.CompilationUnit.PrimaryClassNodeOperation -import org.codehaus.groovy.control.CompilePhase -import org.codehaus.groovy.control.CompilerConfiguration -import org.codehaus.groovy.control.SourceUnit -import org.codehaus.groovy.transform.ASTTransformation - -import java.security.CodeSource - -/* -* This TestHarness exists so that a global transform can be run without -* using the Jar services mechanism, which requires building a jar. -* -* To use this simply create an instance of TransformTestHelper with -* an ASTTransformation and CompilePhase, then invoke parse(File) or -* parse(String). -* -* This test harness is not exactly the same as executing a global transformation -* but can greatly aide in debugging and testing a transform. You should still -* test your global transformation when packaged as a jar service before -* releasing it. -* -* @author Hamlet D'Arcy -*/ -class TransformTestHelper { - - private final ASTTransformation transform - private final CompilePhase phase - - /** - * Creates the test helper. - * @param transform - * the transform to run when compiling the file later - * @param phase - * the phase to run the transform in - */ - def TransformTestHelper(ASTTransformation transform, CompilePhase phase) { - this.transform = transform - this.phase = phase - } - - /** - * Compiles the File into a Class applying the transform specified in the constructor. - * @input input - * must be a groovy source file - */ - public Class parse(File input) { - TestHarnessClassLoader loader = new TestHarnessClassLoader(transform, phase) - return loader.parseClass(input) - } - - /** - * Compiles the String into a Class applying the transform specified in the constructor. - * @input input - * must be a valid groovy source string - */ - public Class parse(String input) { - TestHarnessClassLoader loader = new TestHarnessClassLoader(transform, phase) - return loader.parseClass(input) - } -} - -/** -* ClassLoader exists so that TestHarnessOperation can be wired into the compile. -* -* @author Hamlet D'Arcy -*/ [email protected] class TestHarnessClassLoader extends GroovyClassLoader { - - private final ASTTransformation transform - private final CompilePhase phase - - TestHarnessClassLoader(ASTTransformation transform, CompilePhase phase) { - this.transform = transform - this.phase = phase - } - - protected CompilationUnit createCompilationUnit(CompilerConfiguration config, CodeSource codeSource) { - CompilationUnit cu = super.createCompilationUnit(config, codeSource) - cu.addPhaseOperation(new TestHarnessOperation(transform), phase.getPhaseNumber()) - return cu - } -} - -/** -* Operation exists so that an AstTransformation can be run against the SourceUnit. -* -* @author Hamlet D'Arcy -*/ [email protected] class TestHarnessOperation extends PrimaryClassNodeOperation { - - private final ASTTransformation transform - - def TestHarnessOperation(transform) { - this.transform = transform; - } - - public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) { - transform.visit(null, source) - } -} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/VariableAccessReplacer.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/VariableAccessReplacer.groovy b/src/main/groovy/VariableAccessReplacer.groovy deleted file mode 100644 index d62dc46..0000000 --- a/src/main/groovy/VariableAccessReplacer.groovy +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.tailrec - -import groovy.transform.CompileStatic -import org.codehaus.groovy.ast.ASTNode -import org.codehaus.groovy.ast.expr.VariableExpression - -/** - * Replace all access to variables and args by new variables. - * The variable names to replace as well as their replacement name and type have to be configured - * in nameAndTypeMapping before calling replaceIn(). - * - * The VariableReplacedListener can be set if clients want to react to variable replacement. - * - * @author Johannes Link - */ -@CompileStatic -class VariableAccessReplacer { - - /** - * Nested map of variable accesses to replace - * e.g.: [ - * 'varToReplace': [name: 'newVar', type: TypeOfVar], - * 'varToReplace2': [name: 'newVar2', type: TypeOfVar2], - * ] - */ - Map<String, Map> nameAndTypeMapping = [:] - - VariableReplacedListener listener = VariableReplacedListener.NULL - - void replaceIn(ASTNode root) { - Closure<Boolean> whenParam = { VariableExpression expr -> - return nameAndTypeMapping.containsKey(expr.name) - } - Closure<VariableExpression> replaceWithLocalVariable = { VariableExpression expr -> - Map nameAndType = nameAndTypeMapping[expr.name] - VariableExpression newVar = AstHelper.createVariableReference(nameAndType) - listener.variableReplaced(expr, newVar) - return newVar - } - new VariableExpressionReplacer(when: whenParam, replaceWith: replaceWithLocalVariable).replaceIn(root) - } - -} - -@CompileStatic -interface VariableReplacedListener { - void variableReplaced(VariableExpression oldVar, VariableExpression newVar) - - static VariableReplacedListener NULL = new VariableReplacedListener() { - @Override - void variableReplaced(VariableExpression oldVar, VariableExpression newVar) { - //do nothing - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/VariableExpressionReplacer.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/VariableExpressionReplacer.groovy b/src/main/groovy/VariableExpressionReplacer.groovy deleted file mode 100644 index 1f14490..0000000 --- a/src/main/groovy/VariableExpressionReplacer.groovy +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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.tailrec - -import groovy.transform.CompileStatic -import org.codehaus.groovy.ast.ASTNode -import org.codehaus.groovy.ast.CodeVisitorSupport -import org.codehaus.groovy.ast.expr.BinaryExpression -import org.codehaus.groovy.ast.expr.BooleanExpression -import org.codehaus.groovy.ast.expr.Expression -import org.codehaus.groovy.ast.expr.ExpressionTransformer -import org.codehaus.groovy.ast.expr.VariableExpression -import org.codehaus.groovy.ast.stmt.AssertStatement -import org.codehaus.groovy.ast.stmt.CaseStatement -import org.codehaus.groovy.ast.stmt.DoWhileStatement -import org.codehaus.groovy.ast.stmt.ExpressionStatement -import org.codehaus.groovy.ast.stmt.ForStatement -import org.codehaus.groovy.ast.stmt.IfStatement -import org.codehaus.groovy.ast.stmt.ReturnStatement -import org.codehaus.groovy.ast.stmt.SwitchStatement -import org.codehaus.groovy.ast.stmt.SynchronizedStatement -import org.codehaus.groovy.ast.stmt.ThrowStatement -import org.codehaus.groovy.ast.stmt.WhileStatement - -import java.lang.reflect.Method - -/** - * Tool for replacing VariableExpression instances in an AST by other VariableExpression instances. - * Regardless of a real change taking place in nested expressions, all considered expression (trees) will be replaced. - * This could be optimized to accelerate compilation. - * - * Within @TailRecursive it is used - * - to swap the access of method args with the access to iteration variables - * - to swap the access of iteration variables with the access of temp vars - * - * @author Johannes Link - */ -@CompileStatic -class VariableExpressionReplacer extends CodeVisitorSupport { - - Closure<Boolean> when = { VariableExpression node -> false } - Closure<VariableExpression> replaceWith = { VariableExpression variableExpression -> variableExpression } - - private ExpressionTransformer transformer - - synchronized void replaceIn(ASTNode root) { - transformer = new VariableExpressionTransformer(when: when, replaceWith: replaceWith) - root.visit(this) - } - - public void visitReturnStatement(ReturnStatement statement) { - replaceExpressionPropertyWhenNecessary(statement) - super.visitReturnStatement(statement); - } - - public void visitIfElse(IfStatement ifElse) { - replaceExpressionPropertyWhenNecessary(ifElse, 'booleanExpression', BooleanExpression) - super.visitIfElse(ifElse); - } - - public void visitForLoop(ForStatement forLoop) { - replaceExpressionPropertyWhenNecessary(forLoop, 'collectionExpression') - super.visitForLoop(forLoop); - } - - /** - * It's the only Expression type in which replacing is considered. - * That's an abuse of the class, but I couldn't think of a better way. - */ - public void visitBinaryExpression(BinaryExpression expression) { - //A hack: Only replace right expression b/c ReturnStatementToIterationConverter needs it that way :-/ - replaceExpressionPropertyWhenNecessary(expression, 'rightExpression') - expression.getRightExpression().visit(this); - super.visitBinaryExpression(expression) - } - - public void visitWhileLoop(WhileStatement loop) { - replaceExpressionPropertyWhenNecessary(loop, 'booleanExpression', BooleanExpression) - super.visitWhileLoop(loop); - } - - public void visitDoWhileLoop(DoWhileStatement loop) { - replaceExpressionPropertyWhenNecessary(loop, 'booleanExpression', BooleanExpression) - super.visitDoWhileLoop(loop); - } - - public void visitSwitch(SwitchStatement statement) { - replaceExpressionPropertyWhenNecessary(statement) - super.visitSwitch(statement) - } - - public void visitCaseStatement(CaseStatement statement) { - replaceExpressionPropertyWhenNecessary(statement) - super.visitCaseStatement(statement) - } - - public void visitExpressionStatement(ExpressionStatement statement) { - replaceExpressionPropertyWhenNecessary(statement) - super.visitExpressionStatement(statement); - } - - public void visitThrowStatement(ThrowStatement statement) { - replaceExpressionPropertyWhenNecessary(statement) - super.visitThrowStatement(statement) - } - - public void visitAssertStatement(AssertStatement statement) { - replaceExpressionPropertyWhenNecessary(statement, 'booleanExpression', BooleanExpression) - replaceExpressionPropertyWhenNecessary(statement, 'messageExpression') - super.visitAssertStatement(statement) - } - - public void visitSynchronizedStatement(SynchronizedStatement statement) { - replaceExpressionPropertyWhenNecessary(statement) - super.visitSynchronizedStatement(statement) - } - - private void replaceExpressionPropertyWhenNecessary(ASTNode node, String propName = "expression", Class propClass = Expression) { - Expression expr = getExpression(node, propName) - - if (expr instanceof VariableExpression) { - if (when(expr)) { - VariableExpression newExpr = replaceWith(expr) - replaceExpression(node, propName, propClass, expr, newExpr) - } - } else { - Expression newExpr = expr.transformExpression(transformer) - replaceExpression(node, propName, propClass, expr, newExpr) - } - } - - private void replaceExpression(ASTNode node, String propName, Class propClass, Expression oldExpr, Expression newExpr) { - //Use reflection to enable CompileStatic - String setterName = 'set' + capitalizeFirst(propName) - Method setExpressionMethod = node.class.getMethod(setterName, [propClass].toArray(new Class[1])) - newExpr.setSourcePosition(oldExpr); - newExpr.copyNodeMetaData(oldExpr); - setExpressionMethod.invoke(node, [newExpr].toArray()) - } - - private Expression getExpression(ASTNode node, String propName) { - //Use reflection to enable CompileStatic - String getterName = 'get' + capitalizeFirst(propName) - Method getExpressionMethod = node.class.getMethod(getterName, new Class[0]) - getExpressionMethod.invoke(node, new Object[0]) as Expression - } - - private String capitalizeFirst(String propName) { - propName[0].toUpperCase() + propName[1..-1] - } - - -} - - http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/VariableExpressionTransformer.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/VariableExpressionTransformer.groovy b/src/main/groovy/VariableExpressionTransformer.groovy deleted file mode 100644 index 106a2f1..0000000 --- a/src/main/groovy/VariableExpressionTransformer.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.tailrec - -import groovy.transform.CompileStatic -import org.codehaus.groovy.ast.expr.Expression -import org.codehaus.groovy.ast.expr.ExpressionTransformer -import org.codehaus.groovy.ast.expr.VariableExpression - -/** - * An expression transformer used in the process of replacing the access to variables - * - * @author Johannes Link - */ -@CompileStatic -class VariableExpressionTransformer implements ExpressionTransformer { - - Closure<Boolean> when - Closure<VariableExpression> replaceWith - - @Override - Expression transform(Expression expr) { - if ((expr instanceof VariableExpression) && when(expr)) { - VariableExpression newExpr = replaceWith(expr) - newExpr.setSourcePosition(expr); - newExpr.copyNodeMetaData(expr); - return newExpr - } - return expr.transformExpression(this) - } -} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/beans/Bindable.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/beans/Bindable.java b/src/main/groovy/beans/Bindable.java deleted file mode 100644 index cf0a5c9..0000000 --- a/src/main/groovy/beans/Bindable.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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.beans; - -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; - -/** - * Annotates a groovy property or a class. - * - * When annotating a property it indicates that the property should be a - * bound property according to the JavaBeans spec, announcing to listeners - * that the value has changed. - * <p> - * When annotating a class it indicates that all groovy properties in that - * class should be bound as though each property had the annotation (even - * if it already has it explicitly). - * <p> - * It is a compilation error to place this annotation on a field (that is - * not a property, i.e. has scope visibility modifiers). - * <p> - * If a property with a user defined setter method is annotated the code - * block is wrapped with the needed code to fire off the event. - * <p> - * The following example shows how you can use this annotation on fields - * of a class: - * <pre> - * class Person { - * @groovy.beans.Bindable - * String firstName - * - * @groovy.beans.Bindable - * def zipCode - * } - * </pre> - * The above example will generate code that is similar to the next snippet. - * Notice the difference between a String property and a def/Object property: - * <pre> - * public class Person { - * @groovy.beans.Bindable - * private java.lang.String firstName - * @groovy.beans.Bindable - * private java.lang.Object zipCode - * final private java.beans.PropertyChangeSupport this$propertyChangeSupport - * - * public Person() { - * this$propertyChangeSupport = new java.beans.PropertyChangeSupport(this) - * } - * - * public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { - * this$propertyChangeSupport.addPropertyChangeListener(listener) - * } - * - * public void addPropertyChangeListener(java.lang.String name, java.beans.PropertyChangeListener listener) { - * this$propertyChangeSupport.addPropertyChangeListener(name, listener) - * } - * - * public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { - * this$propertyChangeSupport.removePropertyChangeListener(listener) - * } - * - * public void removePropertyChangeListener(java.lang.String name, java.beans.PropertyChangeListener listener) { - * this$propertyChangeSupport.removePropertyChangeListener(name, listener) - * } - * - * public void firePropertyChange(java.lang.String name, java.lang.Object oldValue, java.lang.Object newValue) { - * this$propertyChangeSupport.firePropertyChange(name, oldValue, newValue) - * } - * - * public java.beans.PropertyChangeListener[] getPropertyChangeListeners() { - * return this$propertyChangeSupport.getPropertyChangeListeners() - * } - * - * public java.beans.PropertyChangeListener[] getPropertyChangeListeners(java.lang.String name) { - * return this$propertyChangeSupport.getPropertyChangeListeners(name) - * } - * - * public void setFirstName(java.lang.String value) { - * this.firePropertyChange('firstName', firstName, firstName = value ) - * } - * - * public void setZipCode(java.lang.Object value) { - * this.firePropertyChange('zipCode', zipCode, zipCode = value ) - * } - * } - * </pre> - * - * @see BindableASTTransformation - * @author Danno Ferrin (shemnon) - */ [email protected] -@Retention(RetentionPolicy.SOURCE) -@Target({ElementType.FIELD, ElementType.TYPE}) -@GroovyASTTransformationClass("groovy.beans.BindableASTTransformation") -public @interface Bindable { -} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/beans/BindableASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/beans/BindableASTTransformation.java b/src/main/groovy/beans/BindableASTTransformation.java deleted file mode 100644 index f84327a..0000000 --- a/src/main/groovy/beans/BindableASTTransformation.java +++ /dev/null @@ -1,428 +0,0 @@ -/* - * 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.beans; - -import org.codehaus.groovy.ast.ASTNode; -import org.codehaus.groovy.ast.AnnotatedNode; -import org.codehaus.groovy.ast.AnnotationNode; -import org.codehaus.groovy.ast.ClassHelper; -import org.codehaus.groovy.ast.ClassNode; -import org.codehaus.groovy.ast.FieldNode; -import org.codehaus.groovy.ast.MethodNode; -import org.codehaus.groovy.ast.Parameter; -import org.codehaus.groovy.ast.PropertyNode; -import org.codehaus.groovy.ast.expr.Expression; -import org.codehaus.groovy.ast.stmt.BlockStatement; -import org.codehaus.groovy.ast.stmt.Statement; -import org.codehaus.groovy.ast.tools.PropertyNodeUtils; -import org.codehaus.groovy.control.CompilePhase; -import org.codehaus.groovy.control.SourceUnit; -import org.codehaus.groovy.control.messages.SimpleMessage; -import org.codehaus.groovy.control.messages.SyntaxErrorMessage; -import org.codehaus.groovy.runtime.MetaClassHelper; -import org.codehaus.groovy.syntax.SyntaxException; -import org.codehaus.groovy.transform.ASTTransformation; -import org.codehaus.groovy.transform.GroovyASTTransformation; -import org.objectweb.asm.Opcodes; - -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; - -import static org.codehaus.groovy.ast.tools.GeneralUtils.args; -import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX; -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.ctorX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.declS; -import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.param; -import static org.codehaus.groovy.ast.tools.GeneralUtils.params; -import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS; -import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt; -import static org.codehaus.groovy.ast.tools.GeneralUtils.varX; - -/** - * Handles generation of code for the {@code @Bindable} annotation when {@code @Vetoable} - * is not present. - * <p> - * Generally, it adds (if needed) a PropertyChangeSupport field and - * the needed add/removePropertyChangeListener methods to support the - * listeners. - * <p> - * It also generates the setter and wires the setter through the - * PropertyChangeSupport. - * <p> - * If a {@link Vetoable} annotation is detected it does nothing and - * lets the {@link VetoableASTTransformation} handle all the changes. - * - * @author Danno Ferrin (shemnon) - * @author Chris Reeves - */ -@GroovyASTTransformation(phase= CompilePhase.CANONICALIZATION) -public class BindableASTTransformation implements ASTTransformation, Opcodes { - - protected static final ClassNode boundClassNode = ClassHelper.make(Bindable.class); - - /** - * Convenience method to see if an annotated node is {@code @Bindable}. - * - * @param node the node to check - * @return true if the node is bindable - */ - public static boolean hasBindableAnnotation(AnnotatedNode node) { - for (AnnotationNode annotation : node.getAnnotations()) { - if (boundClassNode.equals(annotation.getClassNode())) { - return true; - } - } - return false; - } - - /** - * Handles the bulk of the processing, mostly delegating to other methods. - * - * @param nodes the ast nodes - * @param source the source unit for the nodes - */ - public void visit(ASTNode[] nodes, SourceUnit source) { - if (!(nodes[0] instanceof AnnotationNode) || !(nodes[1] instanceof AnnotatedNode)) { - throw new RuntimeException("Internal error: wrong types: $node.class / $parent.class"); - } - AnnotationNode node = (AnnotationNode) nodes[0]; - AnnotatedNode parent = (AnnotatedNode) nodes[1]; - - if (VetoableASTTransformation.hasVetoableAnnotation(parent)) { - // VetoableASTTransformation will handle both @Bindable and @Vetoable - return; - } - - ClassNode declaringClass = parent.getDeclaringClass(); - if (parent instanceof FieldNode) { - if ((((FieldNode) parent).getModifiers() & Opcodes.ACC_FINAL) != 0) { - source.getErrorCollector().addErrorAndContinue(new SyntaxErrorMessage( - new SyntaxException("@groovy.beans.Bindable cannot annotate a final property.", - node.getLineNumber(), node.getColumnNumber(), node.getLastLineNumber(), node.getLastColumnNumber()), - source)); - } - - if (VetoableASTTransformation.hasVetoableAnnotation(parent.getDeclaringClass())) { - // VetoableASTTransformation will handle both @Bindable and @Vetoable - return; - } - addListenerToProperty(source, node, declaringClass, (FieldNode) parent); - } else if (parent instanceof ClassNode) { - addListenerToClass(source, (ClassNode) parent); - } - } - - private void addListenerToProperty(SourceUnit source, AnnotationNode node, ClassNode declaringClass, FieldNode field) { - String fieldName = field.getName(); - for (PropertyNode propertyNode : declaringClass.getProperties()) { - if (propertyNode.getName().equals(fieldName)) { - if (field.isStatic()) { - //noinspection ThrowableInstanceNeverThrown - source.getErrorCollector().addErrorAndContinue(new SyntaxErrorMessage( - new SyntaxException("@groovy.beans.Bindable cannot annotate a static property.", - node.getLineNumber(), node.getColumnNumber(), node.getLastLineNumber(), node.getLastColumnNumber()), - source)); - } else { - if (needsPropertyChangeSupport(declaringClass, source)) { - addPropertyChangeSupport(declaringClass); - } - createListenerSetter(declaringClass, propertyNode); - } - return; - } - } - //noinspection ThrowableInstanceNeverThrown - source.getErrorCollector().addErrorAndContinue(new SyntaxErrorMessage( - new SyntaxException("@groovy.beans.Bindable must be on a property, not a field. Try removing the private, protected, or public modifier.", - node.getLineNumber(), node.getColumnNumber(), node.getLastLineNumber(), node.getLastColumnNumber()), - source)); - } - - private void addListenerToClass(SourceUnit source, ClassNode classNode) { - if (needsPropertyChangeSupport(classNode, source)) { - addPropertyChangeSupport(classNode); - } - for (PropertyNode propertyNode : classNode.getProperties()) { - FieldNode field = propertyNode.getField(); - // look to see if per-field handlers will catch this one... - if (hasBindableAnnotation(field) - || ((field.getModifiers() & Opcodes.ACC_FINAL) != 0) - || field.isStatic() - || VetoableASTTransformation.hasVetoableAnnotation(field)) - { - // explicitly labeled properties are already handled, - // don't transform final properties - // don't transform static properties - // VetoableASTTransformation will handle both @Bindable and @Vetoable - continue; - } - createListenerSetter(classNode, propertyNode); - } - } - - /* - * Wrap an existing setter. - */ - private static void wrapSetterMethod(ClassNode classNode, String propertyName) { - String getterName = "get" + MetaClassHelper.capitalize(propertyName); - MethodNode setter = classNode.getSetterMethod("set" + MetaClassHelper.capitalize(propertyName)); - - if (setter != null) { - // Get the existing code block - Statement code = setter.getCode(); - - Expression oldValue = varX("$oldValue"); - Expression newValue = varX("$newValue"); - BlockStatement block = new BlockStatement(); - - // create a local variable to hold the old value from the getter - block.addStatement(declS(oldValue, callThisX(getterName))); - - // call the existing block, which will presumably set the value properly - block.addStatement(code); - - // get the new value to emit in the event - block.addStatement(declS(newValue, callThisX(getterName))); - - // add the firePropertyChange method call - block.addStatement(stmt(callThisX("firePropertyChange", args(constX(propertyName), oldValue, newValue)))); - - // replace the existing code block with our new one - setter.setCode(block); - } - } - - private void createListenerSetter(ClassNode classNode, PropertyNode propertyNode) { - String setterName = "set" + MetaClassHelper.capitalize(propertyNode.getName()); - if (classNode.getMethods(setterName).isEmpty()) { - Statement setterBlock = createBindableStatement(propertyNode, fieldX(propertyNode.getField())); - - // create method void <setter>(<type> fieldName) - createSetterMethod(classNode, propertyNode, setterName, setterBlock); - } else { - wrapSetterMethod(classNode, propertyNode.getName()); - } - } - - /** - * Creates a statement body similar to: - * <code>this.firePropertyChange("field", field, field = value)</code> - * - * @param propertyNode the field node for the property - * @param fieldExpression a field expression for setting the property value - * @return the created statement - */ - protected Statement createBindableStatement(PropertyNode propertyNode, Expression fieldExpression) { - // create statementBody - return stmt(callThisX("firePropertyChange", args(constX(propertyNode.getName()), fieldExpression, assignX(fieldExpression, varX("value"))))); - } - - /** - * Creates a setter method with the given body. - * - * @param declaringClass the class to which we will add the setter - * @param propertyNode the field to back the setter - * @param setterName the name of the setter - * @param setterBlock the statement representing the setter block - */ - protected void createSetterMethod(ClassNode declaringClass, PropertyNode propertyNode, String setterName, Statement setterBlock) { - MethodNode setter = new MethodNode( - setterName, - PropertyNodeUtils.adjustPropertyModifiersForMethod(propertyNode), - ClassHelper.VOID_TYPE, - params(param(propertyNode.getType(), "value")), - ClassNode.EMPTY_ARRAY, - setterBlock); - setter.setSynthetic(true); - // add it to the class - declaringClass.addMethod(setter); - } - - /** - * Snoops through the declaring class and all parents looking for methods - * <code>void addPropertyChangeListener(PropertyChangeListener)</code>, - * <code>void removePropertyChangeListener(PropertyChangeListener)</code>, and - * <code>void firePropertyChange(String, Object, Object)</code>. If any are defined all - * must be defined or a compilation error results. - * - * @param declaringClass the class to search - * @param sourceUnit the source unit, for error reporting. {@code @NotNull}. - * @return true if property change support should be added - */ - protected boolean needsPropertyChangeSupport(ClassNode declaringClass, SourceUnit sourceUnit) { - boolean foundAdd = false, foundRemove = false, foundFire = false; - ClassNode consideredClass = declaringClass; - while (consideredClass!= null) { - for (MethodNode method : consideredClass.getMethods()) { - // just check length, MOP will match it up - foundAdd = foundAdd || method.getName().equals("addPropertyChangeListener") && method.getParameters().length == 1; - foundRemove = foundRemove || method.getName().equals("removePropertyChangeListener") && method.getParameters().length == 1; - foundFire = foundFire || method.getName().equals("firePropertyChange") && method.getParameters().length == 3; - if (foundAdd && foundRemove && foundFire) { - return false; - } - } - consideredClass = consideredClass.getSuperClass(); - } - // check if a super class has @Bindable annotations - consideredClass = declaringClass.getSuperClass(); - while (consideredClass!=null) { - if (hasBindableAnnotation(consideredClass)) return false; - for (FieldNode field : consideredClass.getFields()) { - if (hasBindableAnnotation(field)) return false; - } - consideredClass = consideredClass.getSuperClass(); - } - if (foundAdd || foundRemove || foundFire) { - sourceUnit.getErrorCollector().addErrorAndContinue( - new SimpleMessage("@Bindable cannot be processed on " - + declaringClass.getName() - + " because some but not all of addPropertyChangeListener, removePropertyChange, and firePropertyChange were declared in the current or super classes.", - sourceUnit) - ); - return false; - } - return true; - } - - /** - * Adds the necessary field and methods to support property change support. - * <p> - * Adds a new field: - * <pre> - * <code>protected final java.beans.PropertyChangeSupport this$PropertyChangeSupport = new java.beans.PropertyChangeSupport(this)</code>" - * </pre> - * <p> - * Also adds support methods: - * <pre> - * <code>public void addPropertyChangeListener(java.beans.PropertyChangeListener)</code> - * <code>public void addPropertyChangeListener(String, java.beans.PropertyChangeListener)</code> - * <code>public void removePropertyChangeListener(java.beans.PropertyChangeListener)</code> - * <code>public void removePropertyChangeListener(String, java.beans.PropertyChangeListener)</code> - * <code>public java.beans.PropertyChangeListener[] getPropertyChangeListeners()</code> - * </pre> - * - * @param declaringClass the class to which we add the support field and methods - */ - protected void addPropertyChangeSupport(ClassNode declaringClass) { - ClassNode pcsClassNode = ClassHelper.make(PropertyChangeSupport.class); - ClassNode pclClassNode = ClassHelper.make(PropertyChangeListener.class); - //String pcsFieldName = "this$propertyChangeSupport"; - - // add field: - // protected final PropertyChangeSupport this$propertyChangeSupport = new java.beans.PropertyChangeSupport(this) - FieldNode pcsField = declaringClass.addField( - "this$propertyChangeSupport", - ACC_FINAL | ACC_PRIVATE | ACC_SYNTHETIC, - pcsClassNode, - ctorX(pcsClassNode, args(varX("this")))); - - // add method: - // void addPropertyChangeListener(listener) { - // this$propertyChangeSupport.addPropertyChangeListener(listener) - // } - declaringClass.addMethod( - new MethodNode( - "addPropertyChangeListener", - ACC_PUBLIC, - ClassHelper.VOID_TYPE, - params(param(pclClassNode, "listener")), - ClassNode.EMPTY_ARRAY, - stmt(callX(fieldX(pcsField), "addPropertyChangeListener", args(varX("listener", pclClassNode)))))); - - // add method: - // void addPropertyChangeListener(name, listener) { - // this$propertyChangeSupport.addPropertyChangeListener(name, listener) - // } - declaringClass.addMethod( - new MethodNode( - "addPropertyChangeListener", - ACC_PUBLIC, - ClassHelper.VOID_TYPE, - params(param(ClassHelper.STRING_TYPE, "name"), param(pclClassNode, "listener")), - ClassNode.EMPTY_ARRAY, - stmt(callX(fieldX(pcsField), "addPropertyChangeListener", args(varX("name", ClassHelper.STRING_TYPE), varX("listener", pclClassNode)))))); - - // add method: - // boolean removePropertyChangeListener(listener) { - // return this$propertyChangeSupport.removePropertyChangeListener(listener); - // } - declaringClass.addMethod( - new MethodNode( - "removePropertyChangeListener", - ACC_PUBLIC, - ClassHelper.VOID_TYPE, - params(param(pclClassNode, "listener")), - ClassNode.EMPTY_ARRAY, - stmt(callX(fieldX(pcsField), "removePropertyChangeListener", args(varX("listener", pclClassNode)))))); - - // add method: void removePropertyChangeListener(name, listener) - declaringClass.addMethod( - new MethodNode( - "removePropertyChangeListener", - ACC_PUBLIC, - ClassHelper.VOID_TYPE, - params(param(ClassHelper.STRING_TYPE, "name"), param(pclClassNode, "listener")), - ClassNode.EMPTY_ARRAY, - stmt(callX(fieldX(pcsField), "removePropertyChangeListener", args(varX("name", ClassHelper.STRING_TYPE), varX("listener", pclClassNode)))))); - - // add method: - // void firePropertyChange(String name, Object oldValue, Object newValue) { - // this$propertyChangeSupport.firePropertyChange(name, oldValue, newValue) - // } - declaringClass.addMethod( - new MethodNode( - "firePropertyChange", - ACC_PUBLIC, - ClassHelper.VOID_TYPE, - params(param(ClassHelper.STRING_TYPE, "name"), param(ClassHelper.OBJECT_TYPE, "oldValue"), param(ClassHelper.OBJECT_TYPE, "newValue")), - ClassNode.EMPTY_ARRAY, - stmt(callX(fieldX(pcsField), "firePropertyChange", args(varX("name", ClassHelper.STRING_TYPE), varX("oldValue"), varX("newValue")))))); - - // add method: - // PropertyChangeListener[] getPropertyChangeListeners() { - // return this$propertyChangeSupport.getPropertyChangeListeners - // } - declaringClass.addMethod( - new MethodNode( - "getPropertyChangeListeners", - ACC_PUBLIC, - pclClassNode.makeArray(), - Parameter.EMPTY_ARRAY, - ClassNode.EMPTY_ARRAY, - returnS(callX(fieldX(pcsField), "getPropertyChangeListeners")))); - - // add method: - // PropertyChangeListener[] getPropertyChangeListeners(String name) { - // return this$propertyChangeSupport.getPropertyChangeListeners(name) - // } - declaringClass.addMethod( - new MethodNode( - "getPropertyChangeListeners", - ACC_PUBLIC, - pclClassNode.makeArray(), - params(param(ClassHelper.STRING_TYPE, "name")), - ClassNode.EMPTY_ARRAY, - returnS(callX(fieldX(pcsField), "getPropertyChangeListeners", args(varX("name", ClassHelper.STRING_TYPE)))))); - } -} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/beans/DefaultPropertyAccessor.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/beans/DefaultPropertyAccessor.java b/src/main/groovy/beans/DefaultPropertyAccessor.java deleted file mode 100644 index 47dae41..0000000 --- a/src/main/groovy/beans/DefaultPropertyAccessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.beans; - -/** - * @author Andres Almiray - */ -public class DefaultPropertyAccessor implements PropertyAccessor { - public static final PropertyAccessor INSTANCE = new DefaultPropertyAccessor(); - - public Object read(Object owner, String propertyName) { - return DefaultPropertyReader.INSTANCE.read(owner, propertyName); - } - - public void write(Object owner, String propertyName, Object value) { - DefaultPropertyWriter.INSTANCE.write(owner, propertyName, value); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/beans/DefaultPropertyReader.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/beans/DefaultPropertyReader.java b/src/main/groovy/beans/DefaultPropertyReader.java deleted file mode 100644 index a03b9a3..0000000 --- a/src/main/groovy/beans/DefaultPropertyReader.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.beans; - -import org.codehaus.groovy.runtime.InvokerHelper; - -/** - * @author Andres Almiray - */ -public class DefaultPropertyReader implements PropertyReader { - public static final PropertyReader INSTANCE = new DefaultPropertyReader(); - - public Object read(Object owner, String propertyName) { - return InvokerHelper.getPropertySafe(owner, propertyName); - } -} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/beans/DefaultPropertyWriter.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/beans/DefaultPropertyWriter.java b/src/main/groovy/beans/DefaultPropertyWriter.java deleted file mode 100644 index 12ac7db..0000000 --- a/src/main/groovy/beans/DefaultPropertyWriter.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.beans; - -import org.codehaus.groovy.runtime.InvokerHelper; - -/** - * @author Andres Almiray - */ -public class DefaultPropertyWriter implements PropertyWriter { - public static final PropertyWriter INSTANCE = new DefaultPropertyWriter(); - - public void write(Object owner, String propertyName, Object value) { - InvokerHelper.setProperty(owner, propertyName, value); - } -} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/beans/ListenerList.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/beans/ListenerList.groovy b/src/main/groovy/beans/ListenerList.groovy deleted file mode 100644 index b8119f1..0000000 --- a/src/main/groovy/beans/ListenerList.groovy +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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.beans - -import org.codehaus.groovy.transform.GroovyASTTransformationClass - -import java.lang.annotation.Documented -import java.lang.annotation.ElementType -import java.lang.annotation.Retention -import java.lang.annotation.RetentionPolicy -import java.lang.annotation.Target - -/** - * This annotation adds Java-style listener support to a class based on an annotated Collection-property. - * <p> - * For any given Collection property, several methods will be written into the enclosing class during the compile phase. These - * changes are visible from Java or other languages. The List is intended to hold listeners of some sort, and the methods - * addListener, removeListener, and getListeners are all added to the class. The actual methods names depend on the generic - * type of the collection. - * <p> - * Given the following example:<br> - * <pre> - * class MyClass { - * @groovy.beans.ListenerList - * List<java.awt.event.ActionListener> listeners - * } - * </pre> - * The following code is generated: - * <pre> - * public class MyClass extends java.lang.Object { - * @groovy.beans.ListenerList - * private java.util.List<java.awt.event.ActionListener> listeners - * - * public void addActionListener(java.awt.event.ActionListener listener) { - * if ( listener == null) { - * return null - * } - * if ( listeners == null) { - * listeners = [] - * } - * listeners.add(listener) - * } - * - * public void removeActionListener(java.awt.event.ActionListener listener) { - * if ( listener == null) { - * return null - * } - * if ( listeners == null) { - * listeners = [] - * } - * listeners.remove(listener) - * } - * - * public java.awt.event.ActionListener[] getActionListeners() { - * java.lang.Object __result = [] - * if ( listeners != null) { - * __result.addAll(listeners) - * } - * return (( __result ) as java.awt.event.ActionListener[]) - * } - * - * public void fireActionPerformed(java.awt.event.ActionEvent param0) { - * if ( listeners != null) { - * def __list = new java.util.ArrayList(listeners) - * for (java.lang.Object listener : __list ) { - * listener.actionPerformed(param0) - * } - * } - * } - * } - * </pre> - * A fire method is created for each public method in the target class. In this case, ActionListener only has one - * method. For a four method interface, four fire methods would be created. - * <p> - * The annotation can take the following parameters: - * <pre> - * name = a suffix for creating the add, remove, and get methods. - * Default: Name of the listener type - * In the above example, if name is set to MyListener, then the class will have an addMyListener, - * removeMyListener, and getMyListeners methods. - * - * synchronize = Whether or not the methods created should be synchronized at the method level. - * Default: false - * </pre> - * <p> - * <strong>Compilation Errors</strong> - Using this annotation incorrectly results in compilation errors rather - * than runtime errors. A list of potential problems includes: - * <ul> - * <li>This annotation can only be applied to a field of type Collection</li> - * <li>The annotated Collection field must have a generic type</li> - * <li>The annotated Collection field must not have a generic wildcard declared</li> - * <li>The generated methods must not already exist</li> - * </ul> - * - * @see ListenerListASTTransformation - * @author Alexander Klein - * @author Hamlet D'Arcy - */ -@Documented -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.FIELD) -@GroovyASTTransformationClass('groovy.beans.ListenerListASTTransformation') -@interface ListenerList { - /** - * A suffix for creating the add, remove, and get methods - * defaulting to the name of the listener type, e.g. if name is set to MyListener, - * then the class will have addMyListener, removeMyListener, and getMyListeners methods. - */ - String name() default "" - - /** - * Whether or not the methods created should be synchronized at the method level. - */ - boolean synchronize() default false -} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/beans/ListenerListASTTransformation.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/beans/ListenerListASTTransformation.groovy b/src/main/groovy/beans/ListenerListASTTransformation.groovy deleted file mode 100644 index 1d7fbf7..0000000 --- a/src/main/groovy/beans/ListenerListASTTransformation.groovy +++ /dev/null @@ -1,384 +0,0 @@ -/* - * 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.beans - -import org.codehaus.groovy.ast.ASTNode -import org.codehaus.groovy.ast.AnnotatedNode -import org.codehaus.groovy.ast.AnnotationNode -import org.codehaus.groovy.ast.ClassHelper -import org.codehaus.groovy.ast.ClassNode -import org.codehaus.groovy.ast.FieldNode -import org.codehaus.groovy.ast.GenericsType -import org.codehaus.groovy.ast.MethodNode -import org.codehaus.groovy.ast.Parameter -import org.codehaus.groovy.ast.VariableScope -import org.codehaus.groovy.ast.expr.ArgumentListExpression -import org.codehaus.groovy.ast.expr.BinaryExpression -import org.codehaus.groovy.ast.expr.BooleanExpression -import org.codehaus.groovy.ast.expr.CastExpression -import org.codehaus.groovy.ast.expr.ConstantExpression -import org.codehaus.groovy.ast.expr.ConstructorCallExpression -import org.codehaus.groovy.ast.expr.DeclarationExpression -import org.codehaus.groovy.ast.expr.ListExpression -import org.codehaus.groovy.ast.expr.MethodCallExpression -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.ExpressionStatement -import org.codehaus.groovy.ast.stmt.ForStatement -import org.codehaus.groovy.ast.stmt.IfStatement -import org.codehaus.groovy.ast.stmt.ReturnStatement -import org.codehaus.groovy.control.CompilePhase -import org.codehaus.groovy.control.SourceUnit -import org.codehaus.groovy.control.messages.SyntaxErrorMessage -import org.codehaus.groovy.syntax.SyntaxException -import org.codehaus.groovy.syntax.Token -import org.codehaus.groovy.syntax.Types -import org.codehaus.groovy.transform.ASTTransformation -import org.codehaus.groovy.transform.GroovyASTTransformation -import org.objectweb.asm.Opcodes - -/** - * Handles generation of code for the {@code @ListenerList} annotation. - * <p> - * Generally, it adds the needed add<Listener>, remove<Listener> and - * get<Listener>s methods to support the Java Beans API. - * <p> - * Additionally it adds corresponding fire<Event> methods. - * <p> - * - * @author Alexander Klein - * @author Hamlet D'Arcy - */ -@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION) -class ListenerListASTTransformation implements ASTTransformation, Opcodes { - private static final Class MY_CLASS = groovy.beans.ListenerList.class - private static final ClassNode COLLECTION_TYPE = ClassHelper.make(Collection) - - public void visit(ASTNode[] nodes, SourceUnit source) { - if (!(nodes[0] instanceof AnnotationNode) || !(nodes[1] instanceof AnnotatedNode)) { - throw new RuntimeException("Internal error: wrong types: ${node.class} / ${parent.class}") - } - AnnotationNode node = nodes[0] - FieldNode field = nodes[1] - ClassNode declaringClass = nodes[1].declaringClass - ClassNode parentClass = field.type - - boolean isCollection = parentClass.isDerivedFrom(COLLECTION_TYPE) || parentClass.implementsInterface(COLLECTION_TYPE) - - if (!isCollection) { - addError(node, source, '@' + MY_CLASS.name + ' can only annotate collection properties.') - return - } - - def types = field.type.genericsTypes - if (!types) { - addError(node, source, '@' + MY_CLASS.name + ' fields must have a generic type.') - return - } - - if (types[0].wildcard) { - addError(node, source, '@' + MY_CLASS.name + ' fields with generic wildcards not yet supported.') - return - } - - def listener = types[0].type - - if (!field.initialValueExpression) { - field.initialValueExpression = new ListExpression() - } - - def name = node.getMember('name')?.value ?: listener.nameWithoutPackage - - def fireList = listener.methods.findAll { MethodNode m -> - m.isPublic() && !m.isSynthetic() && !m.isStatic() - } - - def synchronize = node.getMember('synchronize')?.value ?: false - addAddListener(source, node, declaringClass, field, listener, name, synchronize) - addRemoveListener(source, node, declaringClass, field, listener, name, synchronize) - addGetListeners(source, node, declaringClass, field, listener, name, synchronize) - - fireList.each { MethodNode method -> - addFireMethods(source, node, declaringClass, field, types, synchronize, method) - } - } - - private static def addError(AnnotationNode node, SourceUnit source, String message) { - source.errorCollector.addError( - new SyntaxErrorMessage(new SyntaxException( - message, - node.lineNumber, - node.columnNumber), - source)) - } - - /** - * Adds the add<Listener> method like: - * <pre> - * synchronized void add${name.capitalize}(${listener.name} listener) { - * if (listener == null) - * return - * if (${field.name} == null) - * ${field.name} = [] - * ${field.name}.add(listener) - * } - * </pre> - */ - void addAddListener(SourceUnit source, AnnotationNode node, ClassNode declaringClass, FieldNode field, ClassNode listener, String name, synchronize) { - - def methodModifiers = synchronize ? ACC_PUBLIC | ACC_SYNCHRONIZED : ACC_PUBLIC - def methodReturnType = ClassHelper.make(Void.TYPE) - def methodName = "add${name.capitalize()}" - def cn = ClassHelper.makeWithoutCaching(listener.name) - cn.redirect = listener - def methodParameter = [new Parameter(cn,'listener')] as Parameter[] - - if (declaringClass.hasMethod(methodName, methodParameter)) { - addError node, source, "Conflict using @${MY_CLASS.name}. Class $declaringClass.name already has method $methodName" - return - } - - BlockStatement block = new BlockStatement() - block.addStatements([ - new IfStatement( - new BooleanExpression( - new BinaryExpression( - new VariableExpression('listener'), - Token.newSymbol(Types.COMPARE_EQUAL, 0, 0), - ConstantExpression.NULL - ) - ), - new ReturnStatement(ConstantExpression.NULL), - EmptyStatement.INSTANCE - ), - new IfStatement( - new BooleanExpression( - new BinaryExpression( - new VariableExpression(field.name), - Token.newSymbol(Types.COMPARE_EQUAL, 0, 0), - ConstantExpression.NULL - ) - ), - new ExpressionStatement( - new BinaryExpression( - new VariableExpression(field.name), - Token.newSymbol(Types.EQUAL, 0, 0), - new ListExpression() - ) - ), - EmptyStatement.INSTANCE - ), - new ExpressionStatement( - new MethodCallExpression(new VariableExpression(field.name), new ConstantExpression('add'), new ArgumentListExpression(new VariableExpression('listener'))) - ) - ]) - declaringClass.addMethod(new MethodNode(methodName, methodModifiers, methodReturnType, methodParameter, [] as ClassNode[], block)) - } - - /** - * Adds the remove<Listener> method like: - * <pre> - * synchronized void remove${name.capitalize}(${listener.name} listener) { - * if (listener == null) - * return - * if (${field.name} == null) - * ${field.name} = [] - * ${field.name}.remove(listener) - * } - * </pre> - */ - void addRemoveListener(SourceUnit source, AnnotationNode node, ClassNode declaringClass, FieldNode field, ClassNode listener, String name, synchronize) { - def methodModifiers = synchronize ? ACC_PUBLIC | ACC_SYNCHRONIZED : ACC_PUBLIC - def methodReturnType = ClassHelper.make(Void.TYPE) - def methodName = "remove${name.capitalize()}" - def cn = ClassHelper.makeWithoutCaching(listener.name) - cn.redirect = listener - def methodParameter = [new Parameter(cn,'listener')] as Parameter[] - - if (declaringClass.hasMethod(methodName, methodParameter)) { - addError node, source, "Conflict using @${MY_CLASS.name}. Class $declaringClass.name already has method $methodName" - return - } - - BlockStatement block = new BlockStatement() - block.addStatements([ - new IfStatement( - new BooleanExpression( - new BinaryExpression( - new VariableExpression('listener'), - Token.newSymbol(Types.COMPARE_EQUAL, 0, 0), - ConstantExpression.NULL - ) - ), - new ReturnStatement(ConstantExpression.NULL), - EmptyStatement.INSTANCE - ), - new IfStatement( - new BooleanExpression( - new BinaryExpression( - new VariableExpression(field.name), - Token.newSymbol(Types.COMPARE_EQUAL, 0, 0), - ConstantExpression.NULL - ) - ), - new ExpressionStatement( - new BinaryExpression( - new VariableExpression(field.name), - Token.newSymbol(Types.EQUAL, 0, 0), - new ListExpression() - ) - ), - EmptyStatement.INSTANCE - ), - new ExpressionStatement( - new MethodCallExpression(new VariableExpression(field.name), new ConstantExpression('remove'), new ArgumentListExpression(new VariableExpression("listener"))) - ) - ]) - declaringClass.addMethod(new MethodNode(methodName, methodModifiers, methodReturnType, methodParameter, [] as ClassNode[], block)) - } - - /** - * Adds the get<Listener>s method like: - * <pre> - * synchronized ${name.capitalize}[] get${name.capitalize}s() { - * def __result = [] - * if (${field.name} != null) - * __result.addAll(${field.name}) - * return __result as ${name.capitalize}[] - * } - * </pre> - */ - void addGetListeners(SourceUnit source, AnnotationNode node, ClassNode declaringClass, FieldNode field, ClassNode listener, String name, synchronize) { - def methodModifiers = synchronize ? ACC_PUBLIC | ACC_SYNCHRONIZED : ACC_PUBLIC - def methodReturnType = listener.makeArray() - def methodName = "get${name.capitalize()}s" - def methodParameter = [] as Parameter[] - - if (declaringClass.hasMethod(methodName, methodParameter)) { - addError node, source, "Conflict using @${MY_CLASS.name}. Class $declaringClass.name already has method $methodName" - return - } - - BlockStatement block = new BlockStatement() - block.addStatements([ - new ExpressionStatement( - new DeclarationExpression( - new VariableExpression("__result", ClassHelper.DYNAMIC_TYPE), - Token.newSymbol(Types.EQUALS, 0, 0), - new ListExpression() - )), - new IfStatement( - new BooleanExpression( - new BinaryExpression( - new VariableExpression(field.name), - Token.newSymbol(Types.COMPARE_NOT_EQUAL, 0, 0), - ConstantExpression.NULL - ) - ), - new ExpressionStatement( - new MethodCallExpression(new VariableExpression('__result'), new ConstantExpression('addAll'), new ArgumentListExpression(new VariableExpression(field.name))) - ), - EmptyStatement.INSTANCE - ), - new ReturnStatement( - new CastExpression( - methodReturnType, - new VariableExpression('__result') - ) - ) - ]) - declaringClass.addMethod(new MethodNode(methodName, methodModifiers, methodReturnType, methodParameter, [] as ClassNode[], block)) - } - - /** - * Adds the fire<Event> methods like: - * <pre> - * void fire${fireMethod.capitalize()}(${parameterList.join(', ')}) { - * if (${field.name} != null) { - * def __list = new ArrayList(${field.name}) - * __list.each { listener -> - * listener.$eventMethod(${evt}) - * } - * } - * } - * </pre> - */ - void addFireMethods(SourceUnit source, AnnotationNode node, ClassNode declaringClass, FieldNode field, GenericsType[] types, boolean synchronize, MethodNode method) { - - def methodReturnType = ClassHelper.make(Void.TYPE) - def methodName = "fire${method.name.capitalize()}" - def methodModifiers = synchronize ? ACC_PUBLIC | ACC_SYNCHRONIZED : ACC_PUBLIC - - if (declaringClass.hasMethod(methodName, method.parameters)) { - addError node, source, "Conflict using @${MY_CLASS.name}. Class $declaringClass.name already has method $methodName" - return - } - - def args = new ArgumentListExpression(method.parameters) - - BlockStatement block = new BlockStatement() - def listenerListType = ClassHelper.make(ArrayList).plainNodeReference - listenerListType.setGenericsTypes(types) - block.addStatements([ - new IfStatement( - new BooleanExpression( - new BinaryExpression( - new VariableExpression(field.name), - Token.newSymbol(Types.COMPARE_NOT_EQUAL, 0, 0), - ConstantExpression.NULL - ) - ), - new BlockStatement([ - new ExpressionStatement( - new DeclarationExpression( - new VariableExpression('__list', listenerListType), - Token.newSymbol(Types.EQUALS, 0, 0), - new ConstructorCallExpression(listenerListType, new ArgumentListExpression( - new VariableExpression(field.name) - )) - ) - ), - new ForStatement( - new Parameter(ClassHelper.DYNAMIC_TYPE, 'listener'), - new VariableExpression('__list'), - new BlockStatement([ - new ExpressionStatement( - new MethodCallExpression( - new VariableExpression('listener'), - method.name, - args - ) - ) - ], new VariableScope()) - ) - ], new VariableScope()), - EmptyStatement.INSTANCE - ) - ]) - - def params = method.parameters.collect { - def paramType = ClassHelper.getWrapper(it.type) - def cn = paramType.plainNodeReference - cn.setRedirect(paramType) - new Parameter(cn, it.name) - } - declaringClass.addMethod(methodName, methodModifiers, methodReturnType, params as Parameter[], [] as ClassNode[], block) - } -}
