public void testMethods() throws Exception { ClassNode classNode = new ClassNode("Foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE); classNode.addConstructor(new ConstructorNode(ACC_PUBLIC, null)); Statement statementA = new ReturnStatement(new ConstantExpression("calledA")); Statement statementB = new ReturnStatement(new ConstantExpression("calledB")); Statement emptyStatement = new BlockStatement(); classNode.addMethod( new MethodNode( "a", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, statementA)); classNode.addMethod( new MethodNode( "b", ACC_PUBLIC, null, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, statementB)); classNode.addMethod( new MethodNode( "noReturnMethodA", ACC_PUBLIC, null, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, emptyStatement)); classNode.addMethod( new MethodNode( "noReturnMethodB", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, emptyStatement)); classNode.addMethod( new MethodNode( "c", ACC_PUBLIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, emptyStatement)); Class fooClass = loadClass(classNode); assertTrue("Loaded a new class", fooClass != null); Object bean = fooClass.newInstance(); assertTrue("Created instance of class: " + bean, bean != null); assertCallMethod(bean, "a", "calledA"); assertCallMethod(bean, "b", "calledB"); assertCallMethod(bean, "noReturnMethodA", null); assertCallMethod(bean, "noReturnMethodB", null); assertCallMethod(bean, "c", null); }
/** * Main loop entry. * * <p>First, it delegates to the super visitClass so we can collect the relevant annotations in an * AST tree walk. * * <p>Second, it calls the visit method on the transformation for each relevant annotation found. * * @param classNode the class to visit */ public void visitClass(ClassNode classNode) { // only descend if we have annotations to look for Map<Class<? extends ASTTransformation>, Set<ASTNode>> baseTransforms = classNode.getTransforms(phase); if (!baseTransforms.isEmpty()) { final Map<Class<? extends ASTTransformation>, ASTTransformation> transformInstances = new HashMap<Class<? extends ASTTransformation>, ASTTransformation>(); for (Class<? extends ASTTransformation> transformClass : baseTransforms.keySet()) { try { transformInstances.put(transformClass, transformClass.newInstance()); } catch (InstantiationException e) { source .getErrorCollector() .addError( new SimpleMessage( "Could not instantiate Transformation Processor " + transformClass, // + " declared by " + // annotation.getClassNode().getName(), source)); } catch (IllegalAccessException e) { source .getErrorCollector() .addError( new SimpleMessage( "Could not instantiate Transformation Processor " + transformClass, // + " declared by " + // annotation.getClassNode().getName(), source)); } } // invert the map, is now one to many transforms = new HashMap<ASTNode, List<ASTTransformation>>(); for (Map.Entry<Class<? extends ASTTransformation>, Set<ASTNode>> entry : baseTransforms.entrySet()) { for (ASTNode node : entry.getValue()) { List<ASTTransformation> list = transforms.get(node); if (list == null) { list = new ArrayList<ASTTransformation>(); transforms.put(node, list); } list.add(transformInstances.get(entry.getKey())); } } targetNodes = new LinkedList<ASTNode[]>(); // first pass, collect nodes super.visitClass(classNode); // second pass, call visit on all of the collected nodes for (ASTNode[] node : targetNodes) { for (ASTTransformation snt : transforms.get(node[0])) { if (snt instanceof CompilationUnitAware) { ((CompilationUnitAware) snt).setCompilationUnit(context.getCompilationUnit()); } snt.visit(node, source); } } } }
private static void addPhaseOperationsForGlobalTransforms( CompilationUnit compilationUnit, Map<String, URL> transformNames, boolean isFirstScan) { GroovyClassLoader transformLoader = compilationUnit.getTransformLoader(); for (Map.Entry<String, URL> entry : transformNames.entrySet()) { try { Class gTransClass = transformLoader.loadClass(entry.getKey(), false, true, false); // no inspection unchecked GroovyASTTransformation transformAnnotation = (GroovyASTTransformation) gTransClass.getAnnotation(GroovyASTTransformation.class); if (transformAnnotation == null) { compilationUnit .getErrorCollector() .addWarning( new WarningMessage( WarningMessage.POSSIBLE_ERRORS, "Transform Class " + entry.getKey() + " is specified as a global transform in " + entry.getValue().toExternalForm() + " but it is not annotated by " + GroovyASTTransformation.class.getName() + " the global transform associated with it may fail and cause the compilation to fail.", null, null)); continue; } if (ASTTransformation.class.isAssignableFrom(gTransClass)) { final ASTTransformation instance = (ASTTransformation) gTransClass.newInstance(); if (instance instanceof CompilationUnitAware) { ((CompilationUnitAware) instance).setCompilationUnit(compilationUnit); } CompilationUnit.SourceUnitOperation suOp = new CompilationUnit.SourceUnitOperation() { public void call(SourceUnit source) throws CompilationFailedException { instance.visit(new ASTNode[] {source.getAST()}, source); } }; if (isFirstScan) { compilationUnit.addPhaseOperation(suOp, transformAnnotation.phase().getPhaseNumber()); } else { compilationUnit.addNewPhaseOperation( suOp, transformAnnotation.phase().getPhaseNumber()); } } else { compilationUnit .getErrorCollector() .addError( new SimpleMessage( "Transform Class " + entry.getKey() + " specified at " + entry.getValue().toExternalForm() + " is not an ASTTransformation.", null)); } } catch (Exception e) { compilationUnit .getErrorCollector() .addError( new SimpleMessage( "Could not instantiate global transform class " + entry.getKey() + " specified at " + entry.getValue().toExternalForm() + " because of exception " + e.toString(), null)); } } }