This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY_2_5_X in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push: new da71231417 GROOVY-10654: `ASTTransformationCustomizer`: enum and array parameters da71231417 is described below commit da712314173eb69201ef08d0a850376fba16bdf7 Author: Eric Milles <eric.mil...@thomsonreuters.com> AuthorDate: Fri Jun 10 10:25:33 2022 -0500 GROOVY-10654: `ASTTransformationCustomizer`: enum and array parameters 2_5_X backport --- .../customizers/ASTTransformationCustomizer.groovy | 104 ++++---- .../ASTTransformationCustomizerTest.groovy | 288 +++++++++++---------- 2 files changed, 210 insertions(+), 182 deletions(-) diff --git a/src/main/groovy/org/codehaus/groovy/control/customizers/ASTTransformationCustomizer.groovy b/src/main/groovy/org/codehaus/groovy/control/customizers/ASTTransformationCustomizer.groovy index d6ac4aaccf..40591a3bae 100644 --- a/src/main/groovy/org/codehaus/groovy/control/customizers/ASTTransformationCustomizer.groovy +++ b/src/main/groovy/org/codehaus/groovy/control/customizers/ASTTransformationCustomizer.groovy @@ -18,14 +18,13 @@ */ package org.codehaus.groovy.control.customizers +import groovy.transform.AutoFinal import groovy.transform.CompilationUnitAware 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.expr.ClosureExpression import org.codehaus.groovy.ast.expr.Expression -import org.codehaus.groovy.ast.expr.ListExpression import org.codehaus.groovy.classgen.GeneratorContext import org.codehaus.groovy.control.CompilationUnit import org.codehaus.groovy.control.CompilePhase @@ -38,6 +37,8 @@ import java.lang.annotation.Annotation import static org.codehaus.groovy.ast.tools.GeneralUtils.classX import static org.codehaus.groovy.ast.tools.GeneralUtils.constX +import static org.codehaus.groovy.ast.tools.GeneralUtils.listX +import static org.codehaus.groovy.ast.tools.GeneralUtils.propX /** * This customizer allows applying an AST transformation to a source unit with @@ -82,12 +83,13 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.constX * @since 1.8.0 * */ +@AutoFinal class ASTTransformationCustomizer extends CompilationCustomizer implements CompilationUnitAware { - private final AnnotationNode annotationNode - final ASTTransformation transformation + private boolean applied // global xforms protected CompilationUnit compilationUnit - private boolean applied = false // used for global AST transformations + private final AnnotationNode annotationNode + final ASTTransformation transformation /** * Creates an AST transformation customizer using the specified annotation. The transformation classloader can @@ -98,9 +100,9 @@ class ASTTransformationCustomizer extends CompilationCustomizer implements Compi * @param astTransformationClassName * @param transformationClassLoader */ - ASTTransformationCustomizer(final Class<? extends Annotation> transformationAnnotation, String astTransformationClassName, ClassLoader transformationClassLoader) { + ASTTransformationCustomizer(Class<? extends Annotation> transformationAnnotation, String astTransformationClassName, ClassLoader transformationClassLoader) { super(findPhase(transformationAnnotation, astTransformationClassName, transformationClassLoader)) - final Class<ASTTransformation> clazz = findASTTransformationClass(transformationAnnotation, astTransformationClassName, transformationClassLoader) + Class<ASTTransformation> clazz = findASTTransformationClass(transformationAnnotation, astTransformationClassName, transformationClassLoader) this.transformation = clazz.newInstance() this.annotationNode = new AnnotationNode(ClassHelper.make(transformationAnnotation)) } @@ -112,7 +114,7 @@ class ASTTransformationCustomizer extends CompilationCustomizer implements Compi * @param transformationAnnotation * @param astTransformationClassName */ - ASTTransformationCustomizer(final Class<? extends Annotation> transformationAnnotation, String astTransformationClassName) { + ASTTransformationCustomizer(Class<? extends Annotation> transformationAnnotation, String astTransformationClassName) { this(transformationAnnotation, astTransformationClassName, transformationAnnotation.classLoader) } @@ -126,15 +128,15 @@ class ASTTransformationCustomizer extends CompilationCustomizer implements Compi * @param astTransformationClassName * @param transformationClassLoader */ - ASTTransformationCustomizer(final Map annotationParams, final Class<? extends Annotation> transformationAnnotation, String astTransformationClassName, ClassLoader transformationClassLoader) { + ASTTransformationCustomizer(Map annotationParams, Class<? extends Annotation> transformationAnnotation, String astTransformationClassName, ClassLoader transformationClassLoader) { super(findPhase(transformationAnnotation, astTransformationClassName, transformationClassLoader)) - final Class<ASTTransformation> clazz = findASTTransformationClass(transformationAnnotation, astTransformationClassName, transformationClassLoader) + Class<ASTTransformation> clazz = findASTTransformationClass(transformationAnnotation, astTransformationClassName, transformationClassLoader) this.transformation = clazz.newInstance() this.annotationNode = new AnnotationNode(ClassHelper.make(transformationAnnotation)) this.annotationParameters = annotationParams } - ASTTransformationCustomizer(final Map annotationParams, final Class<? extends Annotation> transformationAnnotation, String astTransformationClassName) { + ASTTransformationCustomizer(Map annotationParams, Class<? extends Annotation> transformationAnnotation, String astTransformationClassName) { this(annotationParams, transformationAnnotation, transformationAnnotation.classLoader) } @@ -144,9 +146,9 @@ class ASTTransformationCustomizer extends CompilationCustomizer implements Compi * @param transformationAnnotation * @param transformationClassLoader */ - ASTTransformationCustomizer(final Class<? extends Annotation> transformationAnnotation, ClassLoader transformationClassLoader) { + ASTTransformationCustomizer(Class<? extends Annotation> transformationAnnotation, ClassLoader transformationClassLoader) { super(findPhase(transformationAnnotation, transformationClassLoader)) - final Class<ASTTransformation> clazz = findASTTransformationClass(transformationAnnotation, transformationClassLoader) + Class<ASTTransformation> clazz = findASTTransformationClass(transformationAnnotation, transformationClassLoader) this.transformation = clazz.newInstance() this.annotationNode = new AnnotationNode(ClassHelper.make(transformationAnnotation)) } @@ -155,14 +157,14 @@ class ASTTransformationCustomizer extends CompilationCustomizer implements Compi * Creates an AST transformation customizer using the specified annotation. * @param transformationAnnotation */ - ASTTransformationCustomizer(final Class<? extends Annotation> transformationAnnotation) { + ASTTransformationCustomizer(Class<? extends Annotation> transformationAnnotation) { this(transformationAnnotation, transformationAnnotation.classLoader) } /** * Creates an AST transformation customizer using the specified transformation. */ - ASTTransformationCustomizer(final ASTTransformation transformation) { + ASTTransformationCustomizer(ASTTransformation transformation) { super(findPhase(transformation)) this.transformation = transformation this.annotationNode = null @@ -175,19 +177,19 @@ class ASTTransformationCustomizer extends CompilationCustomizer implements Compi * @param transformationAnnotation * @param transformationClassLoader */ - ASTTransformationCustomizer(final Map annotationParams, final Class<? extends Annotation> transformationAnnotation, ClassLoader transformationClassLoader) { + ASTTransformationCustomizer(Map annotationParams, Class<? extends Annotation> transformationAnnotation, ClassLoader transformationClassLoader) { super(findPhase(transformationAnnotation, transformationClassLoader)) - final Class<ASTTransformation> clazz = findASTTransformationClass(transformationAnnotation, transformationClassLoader) + Class<ASTTransformation> clazz = findASTTransformationClass(transformationAnnotation, transformationClassLoader) this.transformation = clazz.newInstance() this.annotationNode = new AnnotationNode(ClassHelper.make(transformationAnnotation)) this.annotationParameters = annotationParams } - ASTTransformationCustomizer(final Map annotationParams, final Class<? extends Annotation> transformationAnnotation) { + ASTTransformationCustomizer(Map annotationParams, Class<? extends Annotation> transformationAnnotation) { this(annotationParams, transformationAnnotation, transformationAnnotation.classLoader) } - ASTTransformationCustomizer(final Map annotationParams, final ASTTransformation transformation) { + ASTTransformationCustomizer(Map annotationParams, ASTTransformation transformation) { this(transformation) this.annotationParameters = annotationParams } @@ -198,7 +200,7 @@ class ASTTransformationCustomizer extends CompilationCustomizer implements Compi @SuppressWarnings('ClassForName') private static Class<ASTTransformation> findASTTransformationClass(Class<? extends Annotation> anAnnotationClass, ClassLoader transformationClassLoader) { - final GroovyASTTransformationClass annotation = anAnnotationClass.getAnnotation(GroovyASTTransformationClass) + GroovyASTTransformationClass annotation = anAnnotationClass.getAnnotation(GroovyASTTransformationClass) if (annotation == null) throw new IllegalArgumentException("Provided class doesn't look like an AST @interface") Class[] classes = annotation.classes() @@ -216,8 +218,8 @@ class ASTTransformationCustomizer extends CompilationCustomizer implements Compi private static CompilePhase findPhase(ASTTransformation transformation) { if (transformation == null) throw new IllegalArgumentException('Provided transformation must not be null') - final Class<?> clazz = transformation.class - final GroovyASTTransformation annotation = clazz.getAnnotation(GroovyASTTransformation) + Class<?> clazz = transformation.class + GroovyASTTransformation annotation = clazz.getAnnotation(GroovyASTTransformation) if (annotation == null) throw new IllegalArgumentException("Provided ast transformation is not annotated with $GroovyASTTransformation.name") annotation.phase() @@ -236,57 +238,59 @@ class ASTTransformationCustomizer extends CompilationCustomizer implements Compi } /** - * Specify annotation parameters. For example, if the annotation is : + * Specify annotation parameters. For example, if the annotation is: * <pre>@Log(value='logger')</pre> * You could create an AST transformation customizer and specify the "value" parameter thanks to this method: - * <pre>annotationParameters = [value: 'logger'] + * <pre>annotationParameters = [value: 'logger']</pre> * * Note that you cannot specify annotation closure values directly. If the annotation you want to add takes * a closure as an argument, you will have to set a {@link ClosureExpression} instead. This can be done by either * creating a custom {@link ClosureExpression} from code, or using the {@link org.codehaus.groovy.ast.builder.AstBuilder}. - * - * Here is an example : + * <p> + * Here is an example: * <pre> - * // add @Contract({distance >= 0 }) - * customizer = new ASTTransformationCustomizer(Contract) - * final expression = new AstBuilder().buildFromCode(CompilePhase.CONVERSION) {-> - * distance >= 0 - * }.expression[0] - * customizer.annotationParameters = [value: expression]</pre> + * // add @Contract({distance >= 0 }) + * def customizer = new ASTTransformationCustomizer(Contract) + * def expression = new AstBuilder().buildFromCode(CompilePhase.CONVERSION) { -> + * distance >= 0 + * }.expression[0] + * customizer.annotationParameters = [value: expression]</pre> * * @param params the annotation parameters * * @since 1.8.1 */ - @SuppressWarnings('Instanceof') void setAnnotationParameters(Map<String, Object> params) { - if (params == null || annotationNode == null) return - params.each { key, value -> - if (!annotationNode.classNode.getMethod(key)) { - throw new IllegalArgumentException("${annotationNode.classNode.name} does not accept any [$key] parameter") + if (annotationNode == null || params == null || params.isEmpty()) return + params.each { name, value -> + if (!annotationNode.classNode.getMethod(name)) { + throw new IllegalArgumentException("${annotationNode.classNode.name} does not accept any [$name] parameter") } if (value instanceof Closure) { - throw new IllegalArgumentException('Direct usage of closure is not supported by the AST ' + - 'compilation customizer. Please use ClosureExpression instead.') - } else if (value instanceof Expression) { + throw new IllegalArgumentException('Direct usage of closure is not supported by the AST compilation customizer. Please use ClosureExpression instead.') + } + + Expression valueExpression + + if (value instanceof Expression) { + valueExpression = value // avoid NPEs due to missing source code - value.lineNumber = 0 - value.lastLineNumber = 0 - annotationNode.addMember(key, value) + value.lineNumber = 0; value.lastLineNumber = 0 } else if (value instanceof Class) { - annotationNode.addMember(key, classX(value)) - } else if (value instanceof List) { - annotationNode.addMember(key, new ListExpression(value.collect { - it instanceof Class ? classX(it) : constX(it) - })) + valueExpression = classX(value) + } else if (value instanceof Enum) { + valueExpression = propX(classX(ClassHelper.make(value.getClass())), value.toString()) + } else if (value instanceof List || value.getClass().isArray()) { + valueExpression = listX(value.collect { it instanceof Class ? classX(it) : constX(it) }) } else { - annotationNode.addMember(key, constX(value)) + valueExpression = constX(value) } + + annotationNode.addMember(name, valueExpression) } } @Override - @SuppressWarnings('Instanceof') void call(SourceUnit source, GeneratorContext context, ClassNode classNode) { if (transformation instanceof CompilationUnitAware) { transformation.compilationUnit = compilationUnit diff --git a/src/test/org/codehaus/groovy/control/customizers/ASTTransformationCustomizerTest.groovy b/src/test/org/codehaus/groovy/control/customizers/ASTTransformationCustomizerTest.groovy index 8b6caa6ad1..cb30c55f9e 100644 --- a/src/test/org/codehaus/groovy/control/customizers/ASTTransformationCustomizerTest.groovy +++ b/src/test/org/codehaus/groovy/control/customizers/ASTTransformationCustomizerTest.groovy @@ -18,224 +18,248 @@ */ package org.codehaus.groovy.control.customizers +import groovy.transform.ConditionalInterrupt import groovy.transform.TimedInterrupt +import groovy.util.logging.Log +import org.codehaus.groovy.ast.ASTNode import org.codehaus.groovy.ast.ClassHelper -import org.codehaus.groovy.ast.expr.ClassExpression -import org.codehaus.groovy.ast.expr.PropertyExpression +import org.codehaus.groovy.ast.ClassNode +import org.codehaus.groovy.ast.builder.AstBuilder +import org.codehaus.groovy.control.CompilePhase import org.codehaus.groovy.control.CompilerConfiguration -import groovy.util.logging.Log - -import java.util.concurrent.TimeUnit -import java.util.logging.Logger +import org.codehaus.groovy.control.SourceUnit import org.codehaus.groovy.transform.ASTTransformation import org.codehaus.groovy.transform.GroovyASTTransformation -import org.codehaus.groovy.control.CompilePhase -import java.util.concurrent.atomic.AtomicBoolean -import org.codehaus.groovy.ast.ASTNode -import org.codehaus.groovy.control.SourceUnit -import java.lang.annotation.Retention -import java.lang.annotation.Target import org.codehaus.groovy.transform.GroovyASTTransformationClass +import org.junit.Test + import java.lang.annotation.ElementType +import java.lang.annotation.Retention import java.lang.annotation.RetentionPolicy -import org.codehaus.groovy.ast.ClassNode -import org.objectweb.asm.Opcodes -import org.codehaus.groovy.ast.builder.AstBuilder -import groovy.transform.ConditionalInterrupt +import java.lang.annotation.Target +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean + +import static groovy.test.GroovyAssert.shouldFail +import static org.codehaus.groovy.ast.tools.GeneralUtils.classX +import static org.codehaus.groovy.ast.tools.GeneralUtils.propX /** * Tests the {@link ASTTransformationCustomizer}. */ -class ASTTransformationCustomizerTest extends GroovyTestCase { - CompilerConfiguration configuration - ASTTransformationCustomizer customizer +final class ASTTransformationCustomizerTest { - void setUp() { - configuration = new CompilerConfiguration() - } + private final CompilerConfiguration config = new CompilerConfiguration() + private final GroovyShell shell = new GroovyShell(config) + @Test void testLocalTransformation() { - customizer = new ASTTransformationCustomizer(Log) - configuration.addCompilationCustomizers(customizer) - def shell = new GroovyShell(configuration) - def result = shell.evaluate(""" + def customizer = new ASTTransformationCustomizer(Log) + config.addCompilationCustomizers(customizer) + def result = shell.evaluate ''' class MyClass {} new MyClass() - """) - assert result.log.class == Logger + ''' + assert result.log.class == java.util.logging.Logger } - void testLocalTransformationAndCustomClassLoader() { - ClassLoader loader = new URLClassLoader([]as URL[]) { + @Test + void testLocalTransformationClassLoader() { + def loader = new URLClassLoader() { @Override Class<?> loadClass(String name) { - null } } shouldFail(ClassNotFoundException) { - customizer = new ASTTransformationCustomizer(Log, loader) + new ASTTransformationCustomizer(Log, loader) } } - void testLocalTransformationWithAnnotationParameter() { - customizer = new ASTTransformationCustomizer(Log) + @Test + void testLocalTransformationStringParameter() { + def customizer = new ASTTransformationCustomizer(Log) customizer.annotationParameters = [value: 'logger'] - configuration.addCompilationCustomizers(customizer) - def shell = new GroovyShell(configuration) - def result = shell.evaluate(""" + config.addCompilationCustomizers(customizer) + def result = shell.evaluate ''' class MyClass {} new MyClass() - """) - assert result.logger.class == Logger + ''' + assert result.logger.class == java.util.logging.Logger } - void testLocalTransformationWithInvalidAnnotationParameter() { - customizer = new ASTTransformationCustomizer(Log) + @Test + void testLocalTransformationUnknownParameter() { + def customizer = new ASTTransformationCustomizer(Log) shouldFail(IllegalArgumentException) { customizer.annotationParameters = [invalid: 'logger'] } } - void testLocalTransformationWithClosureAnnotationParameter() { - // add @Contract({distance = 1 }) - customizer = new ASTTransformationCustomizer(Contract) - final expression = new AstBuilder().buildFromCode(CompilePhase.CONVERSION) {-> - distance = 1 - }.expression[0] + @Test + void testLocalTransformationListOfClassParameter() { + def customizer = new ASTTransformationCustomizer(Newify) + customizer.annotationParameters = [value: [Integer, Long]] + config.addCompilationCustomizers(customizer) + def result = shell.evaluate ''' + Integer(11) + Long(31) + ''' + assert result == 42 + } + + @Test + void testLocalTransformationArrayOfClassParameter() { + def customizer = new ASTTransformationCustomizer(Newify) + customizer.annotationParameters = [value: [Integer, Long] as Class[]] + config.addCompilationCustomizers(customizer) + def result = shell.evaluate ''' + Integer(11) + Long(31) + ''' + assert result == 42 + } + + @Test + void testLocalTransformationClosureExpressionParameter() { + // add @ConditionalInterrupt(value={ true }, thrown=SecurityException) + def expression = new AstBuilder().buildFromCode(CompilePhase.CONVERSION) { -> true }.expression[0] + def customizer = new ASTTransformationCustomizer(ConditionalInterrupt, value: expression, thrown: SecurityException) + config.addCompilationCustomizers(customizer) + shouldFail(SecurityException) { + shell.evaluate ''' + class MyClass { + void doIt() { } + } + new MyClass().doIt() + ''' + } + } + + @Test + void testLocalTransformationClosureExpressionParameter2() { + // add @Contract({ distance = 1 }) + def expression = new AstBuilder().buildFromCode(CompilePhase.CONVERSION) { -> distance = 1 }.expression[0] + def customizer = new ASTTransformationCustomizer(Contract) customizer.annotationParameters = [value: expression] - configuration.addCompilationCustomizers(customizer) - def shell = new GroovyShell(configuration) - def result = shell.evaluate(""" + config.addCompilationCustomizers(customizer) + def result = shell.evaluate ''' class MyClass { int distance MyClass() {} } new MyClass() - """) + ''' assert result.distance == 1 } - void testLocalTransformationWithClosureAnnotationParameter_notAnnotatedAsASTInterface() { - // add @Contract2({distance = 1 }) - customizer = new ASTTransformationCustomizer(Contract2, "org.codehaus.groovy.control.customizers.ContractAnnotation") - final expression = new AstBuilder().buildFromCode(CompilePhase.CONVERSION) {-> - distance = 1 - }.expression[0] + @Test + void testLocalTransformationClosureExpressionParameter3() { + // add @Contract2({ distance = 1 }) + def expression = new AstBuilder().buildFromCode(CompilePhase.CONVERSION) { -> distance = 1 }.expression[0] + def customizer = new ASTTransformationCustomizer(Contract2, 'org.codehaus.groovy.control.customizers.ContractAnnotation') customizer.annotationParameters = [value: expression] - configuration.addCompilationCustomizers(customizer) - def shell = new GroovyShell(configuration) - def result = shell.evaluate(""" + config.addCompilationCustomizers(customizer) + def result = shell.evaluate ''' class MyClass { int distance MyClass() {} } new MyClass() - """) + ''' assert result.distance == 1 } - void testLocalTransformationWithClassAnnotationParameter() { - // add @ConditionalInterrupt(value={ true }, thrown=SecurityException) - final expression = new AstBuilder().buildFromCode(CompilePhase.CONVERSION) {-> - true - }.expression[0] - customizer = new ASTTransformationCustomizer(ConditionalInterrupt, value:expression, thrown:SecurityException) - configuration.addCompilationCustomizers(customizer) - def shell = new GroovyShell(configuration) - shouldFail(SecurityException) { - shell.evaluate(""" - class MyClass { - void doIt() { } + @Test + void testLocalTransformationPropertyExpressionParameter() { + def customizer = new ASTTransformationCustomizer(TimedInterrupt) + customizer.annotationParameters = [value: 300, unit: propX(classX(ClassHelper.make(TimeUnit)),'MILLISECONDS')] + config.addCompilationCustomizers(customizer) + assert shell.evaluate('''import java.util.concurrent.TimeoutException + boolean interrupted = false + try { + 10.times { + sleep 100 } - new MyClass().doIt() - """) - } + } catch (TimeoutException ignore) { + interrupted = true + } + interrupted + ''') } + @Test // GROOVY-10654 + void testLocalTransformationEnumerationConstantParameter() { + def customizer = new ASTTransformationCustomizer(TimedInterrupt) + customizer.annotationParameters = [value: 300, unit: TimeUnit.MILLISECONDS] + config.addCompilationCustomizers(customizer) + assert shell.evaluate('''import java.util.concurrent.TimeoutException + boolean interrupted = false + try { + 10.times { + sleep 100 + } + } catch (TimeoutException ignore) { + interrupted = true + } + interrupted + ''') + } + + //-------------------------------------------------------------------------- + + @Test void testGlobalTransformation() { - final TestTransformation transformation = new TestTransformation() - customizer = new ASTTransformationCustomizer(transformation) - configuration.addCompilationCustomizers(customizer) - def shell = new GroovyShell(configuration) + TestTransformation transformation = new TestTransformation() + config.addCompilationCustomizers(new ASTTransformationCustomizer(transformation)) assert shell.evaluate('true') - assert transformation.applied.get() + assert transformation.applied } + @Test void testGlobalTransformation2() { - final TestTransformation transformation = new TestTransformation() - customizer = new ASTTransformationCustomizer(transformation) - configuration.addCompilationCustomizers(customizer) - def shell = new GroovyShell(configuration) - assert shell.evaluate(""" + TestTransformation transformation = new TestTransformation() + config.addCompilationCustomizers(new ASTTransformationCustomizer(transformation)) + assert shell.evaluate(''' class A {} class B {} true - """) - assert transformation.applied.get() - } - - void testLocalTransformationWithListOfClassAnnotationParameter() { - customizer = new ASTTransformationCustomizer(Newify, value: [Integer, Long]) - configuration.addCompilationCustomizers(customizer) - def shell = new GroovyShell(configuration) - def result = shell.evaluate ''' - Integer(11) + Long(31) - ''' - assert result == 42 - } - - void testAnyExpressionAsParameterValue() { - customizer = new ASTTransformationCustomizer(value:100, unit: new PropertyExpression(new ClassExpression(ClassHelper.make(TimeUnit)),'MILLISECONDS'), TimedInterrupt) - configuration.addCompilationCustomizers(customizer) - def shell = new GroovyShell(configuration) - def result = shell.evaluate '''import java.util.concurrent.TimeoutException - -boolean interrupted = false -try { - 100.times { - Thread.sleep(100) - } -} catch (TimeoutException e) { - interrupted = true -} - -interrupted''' - assert result + ''') + assert transformation.applied } @GroovyASTTransformation(phase=CompilePhase.CONVERSION) private static class TestTransformation implements ASTTransformation { - private AtomicBoolean applied = new AtomicBoolean(false) + private final applied = new AtomicBoolean() + + boolean isApplied() { return applied.get() } + @Override void visit(ASTNode[] nodes, SourceUnit source) { if (applied.getAndSet(true)) { - throw new Exception("Global AST transformation should only be applied once") + throw new Exception('Global AST transformation should only be applied once') } } - } - -} - -@Retention(RetentionPolicy.SOURCE) -@Target([ElementType.TYPE]) -@GroovyASTTransformationClass("org.codehaus.groovy.control.customizers.ContractAnnotation") -protected @interface Contract { - Class value(); } @GroovyASTTransformation(phase=CompilePhase.CONVERSION) -protected class ContractAnnotation implements ASTTransformation, Opcodes { +protected class ContractAnnotation implements ASTTransformation { + @Override void visit(ASTNode[] nodes, SourceUnit source) { def node = nodes[0] - def member = node.getMember("value") - ((ClassNode)nodes[1]).getDeclaredConstructors()[0].code = member.code + def member = node.getMember('value') + ((ClassNode) nodes[1]).getDeclaredConstructors()[0].code = member.code } } +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +@GroovyASTTransformationClass('org.codehaus.groovy.control.customizers.ContractAnnotation') +protected @interface Contract { + Class value(); +} + +@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) -@Target([ElementType.TYPE]) protected @interface Contract2 { Class value(); }