|
Groovy example source code file (ListenerListASTTransformation.groovy)
The Groovy ListenerListASTTransformation.groovy source code/* * Copyright 2003-2011 the original author or authors. * * Licensed 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.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 import org.codehaus.groovy.ast.* import org.codehaus.groovy.ast.expr.* import org.codehaus.groovy.ast.stmt.* /** * 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: * <p/> * <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), new EmptyStatement() ), 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() ) ), new EmptyStatement() ), 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: * <p/> * <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), new EmptyStatement() ), 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() ) ), new EmptyStatement() ), 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: * <p/> * <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))) ), new EmptyStatement() ), 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: * <p/> * <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) 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()), new EmptyStatement() ) ]) def params = method.parameters.collect { def paramType = ClassHelper.getWrapper(it.type) def cn = ClassHelper.makeWithoutCaching(paramType.name) cn.setRedirect(paramType) new Parameter(cn, it.name) } declaringClass.addMethod(methodName, methodModifiers, methodReturnType, params as Parameter[], [] as ClassNode[], block) } } Other Groovy examples (source code examples)Here is a short list of links related to this Groovy ListenerListASTTransformation.groovy source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.