From fd2583ed28e84d3d43aa5a81e8dbaa2e54e3a83a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 18 Nov 2013 12:40:57 +0000 Subject: [PATCH] Support concise @Grab on all types of imports Previously, the automatic addition of the group and version to a @Grab annotation based on the module name would only work on standard import statements. This commit adds support for this functionality on wildcard imports, static imports and wildcard static imports. All of the following are now supported: @Grab('spring-core') import org.springframework.util.Assert @Grab('spring-core') import org.springframework.util.* @Grab('spring-core') import static org.springframework.util.Assert.isTrue @Grab('spring-core') import static org.springframework.util.Assert.* --- ...veDependencyCoordinatesTransformation.java | 26 +- ...endencyCoordinatesTransformationTests.java | 233 ++++++++++++++++++ 2 files changed, 256 insertions(+), 3 deletions(-) create mode 100644 spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/transformation/ResolveDependencyCoordinatesTransformationTests.java diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/transformation/ResolveDependencyCoordinatesTransformation.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/transformation/ResolveDependencyCoordinatesTransformation.java index 5b9e9c7d604..908a869fc12 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/transformation/ResolveDependencyCoordinatesTransformation.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/transformation/ResolveDependencyCoordinatesTransformation.java @@ -21,6 +21,7 @@ import groovy.lang.Grab; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.Map; import java.util.Set; import org.codehaus.groovy.ast.ASTNode; @@ -60,9 +61,24 @@ public class ResolveDependencyCoordinatesTransformation implements ASTTransforma for (ASTNode node : nodes) { if (node instanceof ModuleNode) { ModuleNode module = (ModuleNode) node; + + visitAnnotatedNode(module.getPackage()); + for (ImportNode importNode : module.getImports()) { visitAnnotatedNode(importNode); } + for (ImportNode importNode : module.getStarImports()) { + visitAnnotatedNode(importNode); + } + for (Map.Entry entry : module.getStaticImports() + .entrySet()) { + visitAnnotatedNode(entry.getValue()); + } + for (Map.Entry entry : module.getStaticStarImports() + .entrySet()) { + visitAnnotatedNode(entry.getValue()); + } + for (ClassNode classNode : module.getClasses()) { visitAnnotatedNode(classNode); classNode.visitContents(classVisitor); @@ -72,9 +88,12 @@ public class ResolveDependencyCoordinatesTransformation implements ASTTransforma } private void visitAnnotatedNode(AnnotatedNode annotatedNode) { - for (AnnotationNode annotationNode : annotatedNode.getAnnotations()) { - if (GRAB_ANNOTATION_NAMES.contains(annotationNode.getClassNode().getName())) { - transformGrabAnnotation(annotationNode); + if (annotatedNode != null) { + for (AnnotationNode annotationNode : annotatedNode.getAnnotations()) { + if (GRAB_ANNOTATION_NAMES.contains(annotationNode.getClassNode() + .getName())) { + transformGrabAnnotation(annotationNode); + } } } } @@ -138,5 +157,6 @@ public class ResolveDependencyCoordinatesTransformation implements ASTTransforma public void visitAnnotations(AnnotatedNode node) { visitAnnotatedNode(node); } + } } diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/transformation/ResolveDependencyCoordinatesTransformationTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/transformation/ResolveDependencyCoordinatesTransformationTests.java new file mode 100644 index 00000000000..1ecf7575a6f --- /dev/null +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/transformation/ResolveDependencyCoordinatesTransformationTests.java @@ -0,0 +1,233 @@ +/* + * Copyright 2012-2013 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 org.springframework.boot.cli.compiler.transformation; + +import groovy.lang.Grab; + +import java.util.Arrays; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ConstructorNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.ast.PackageNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.VariableScope; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.io.ReaderSource; +import org.codehaus.groovy.transform.ASTTransformation; +import org.junit.Before; +import org.junit.Test; +import org.springframework.boot.cli.compiler.ArtifactCoordinatesResolver; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link ResolveDependencyCoordinatesTransformation} + * + * @author Andy Wilkinson + */ +public final class ResolveDependencyCoordinatesTransformationTests { + + private final SourceUnit sourceUnit = new SourceUnit((String) null, + (ReaderSource) null, null, null, null); + + private final ModuleNode moduleNode = new ModuleNode(this.sourceUnit); + + private final AnnotationNode grabAnnotation = createGrabAnnotation(); + + private final ArtifactCoordinatesResolver coordinatesResolver = mock(ArtifactCoordinatesResolver.class); + + private final ASTTransformation transformation = new ResolveDependencyCoordinatesTransformation( + this.coordinatesResolver); + + @Before + public void setupExpectations() { + when(this.coordinatesResolver.getGroupId("spring-core")).thenReturn( + "org.springframework"); + when(this.coordinatesResolver.getVersion("spring-core")).thenReturn("4.0.0.RC1"); + } + + @Test + public void transformationOfAnnotationOnImport() { + this.moduleNode.addImport(null, null, Arrays.asList(this.grabAnnotation)); + assertGrabAnnotationHasBeenTransformation(); + } + + @Test + public void transformationOfAnnotationOnStarImport() { + this.moduleNode.addStarImport("org.springframework.util", + Arrays.asList(this.grabAnnotation)); + + assertGrabAnnotationHasBeenTransformation(); + } + + @Test + public void transformationOfAnnotationOnStaticImport() { + this.moduleNode.addStaticImport(null, null, null, + Arrays.asList(this.grabAnnotation)); + + assertGrabAnnotationHasBeenTransformation(); + } + + @Test + public void transformationOfAnnotationOnStaticStarImport() { + this.moduleNode.addStaticStarImport(null, null, + Arrays.asList(this.grabAnnotation)); + + assertGrabAnnotationHasBeenTransformation(); + } + + @Test + public void transformationOfAnnotationOnPackage() { + PackageNode packageNode = new PackageNode("test"); + packageNode.addAnnotation(this.grabAnnotation); + this.moduleNode.setPackage(packageNode); + + assertGrabAnnotationHasBeenTransformation(); + } + + @Test + public void transformationOfAnnotationOnClass() { + ClassNode classNode = new ClassNode("Test", 0, new ClassNode(Object.class)); + classNode.addAnnotation(this.grabAnnotation); + this.moduleNode.addClass(classNode); + + assertGrabAnnotationHasBeenTransformation(); + } + + @Test + public void transformationOfAnnotationOnAnnotation() { + } + + @Test + public void transformationOfAnnotationOnField() { + ClassNode classNode = new ClassNode("Test", 0, new ClassNode(Object.class)); + this.moduleNode.addClass(classNode); + + FieldNode fieldNode = new FieldNode("test", 0, new ClassNode(Object.class), + classNode, null); + classNode.addField(fieldNode); + + fieldNode.addAnnotation(this.grabAnnotation); + + assertGrabAnnotationHasBeenTransformation(); + } + + @Test + public void transformationOfAnnotationOnConstructor() { + ClassNode classNode = new ClassNode("Test", 0, new ClassNode(Object.class)); + this.moduleNode.addClass(classNode); + + ConstructorNode constructorNode = new ConstructorNode(0, null); + constructorNode.addAnnotation(this.grabAnnotation); + classNode.addMethod(constructorNode); + + assertGrabAnnotationHasBeenTransformation(); + } + + @Test + public void transformationOfAnnotationOnMethod() { + ClassNode classNode = new ClassNode("Test", 0, new ClassNode(Object.class)); + this.moduleNode.addClass(classNode); + + MethodNode methodNode = new MethodNode("test", 0, new ClassNode(Void.class), + new Parameter[0], new ClassNode[0], null); + methodNode.addAnnotation(this.grabAnnotation); + classNode.addMethod(methodNode); + + assertGrabAnnotationHasBeenTransformation(); + } + + @Test + public void transformationOfAnnotationOnMethodParameter() { + ClassNode classNode = new ClassNode("Test", 0, new ClassNode(Object.class)); + this.moduleNode.addClass(classNode); + + Parameter parameter = new Parameter(new ClassNode(Object.class), "test"); + parameter.addAnnotation(this.grabAnnotation); + + MethodNode methodNode = new MethodNode("test", 0, new ClassNode(Void.class), + new Parameter[] { parameter }, new ClassNode[0], null); + classNode.addMethod(methodNode); + + assertGrabAnnotationHasBeenTransformation(); + } + + @Test + public void transformationOfAnnotationOnLocalVariable() { + ClassNode classNode = new ClassNode("Test", 0, new ClassNode(Object.class)); + this.moduleNode.addClass(classNode); + + DeclarationExpression declarationExpression = new DeclarationExpression( + new VariableExpression("test"), null, new ConstantExpression("test")); + declarationExpression.addAnnotation(this.grabAnnotation); + + BlockStatement code = new BlockStatement( + Arrays.asList((Statement) new ExpressionStatement(declarationExpression)), + new VariableScope()); + + MethodNode methodNode = new MethodNode("test", 0, new ClassNode(Void.class), + new Parameter[0], new ClassNode[0], code); + + classNode.addMethod(methodNode); + + assertGrabAnnotationHasBeenTransformation(); + } + + private AnnotationNode createGrabAnnotation() { + ClassNode classNode = new ClassNode(Grab.class); + AnnotationNode annotationNode = new AnnotationNode(classNode); + annotationNode.addMember("value", new ConstantExpression("spring-core")); + return annotationNode; + } + + private void assertGrabAnnotationHasBeenTransformation() { + this.transformation.visit(new ASTNode[] { this.moduleNode }, this.sourceUnit); + + assertEquals("org.springframework", getGrabAnnotationMemberAsString("group")); + assertEquals("spring-core", getGrabAnnotationMemberAsString("module")); + assertEquals("4.0.0.RC1", getGrabAnnotationMemberAsString("version")); + } + + private Object getGrabAnnotationMemberAsString(String memberName) { + Expression expression = this.grabAnnotation.getMember(memberName); + if (expression instanceof ConstantExpression) { + return ((ConstantExpression) expression).getValue(); + } + else if (expression == null) { + return null; + } + else { + throw new IllegalStateException("Member '" + memberName + + "' is not a ConstantExpression"); + } + } + +}