forked from spring-projects/spring-boot
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a convenience AstTransformation base class for BOMs
Plugin authors can extend this class, provide missing methods, and specify a BOM to add to the dependency management lookup (i.e. dependencies by artifactId)
- Loading branch information
Showing
2 changed files
with
264 additions
and
0 deletions.
There are no files selected for viewing
141 changes: 141 additions & 0 deletions
141
...-cli/src/main/java/org/springframework/boot/cli/compiler/GenericBomAstTransformation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
/* | ||
* Copyright 2014-2015 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; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
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.ModuleNode; | ||
import org.codehaus.groovy.ast.PackageNode; | ||
import org.codehaus.groovy.ast.expr.ConstantExpression; | ||
import org.codehaus.groovy.ast.expr.Expression; | ||
import org.codehaus.groovy.ast.expr.ListExpression; | ||
import org.codehaus.groovy.control.CompilePhase; | ||
import org.codehaus.groovy.control.SourceUnit; | ||
import org.codehaus.groovy.transform.GroovyASTTransformation; | ||
import org.springframework.boot.groovy.DependencyManagementBom; | ||
import org.springframework.core.Ordered; | ||
|
||
/** | ||
* A base class that lets plugin authors easily add additional BOMs to all apps. All the | ||
* dependencies in the bom (and it's transitives) will be added to the dependency | ||
* management lookup, so an app can use just the artifact id (e.g. "spring-jdbc") in a | ||
* <code>@Grab</code>. To install, implement the missing methods and list the class in | ||
* <code>META-INF/services/org.springframework.boot.cli.compiler.SpringBootAstTransformation</code> | ||
* . The {@link #getOrder()} value needs to be before | ||
* {@link DependencyManagementBomTransformation#ORDER}. | ||
* | ||
* @author Dave Syer | ||
* | ||
*/ | ||
@GroovyASTTransformation(phase = CompilePhase.CONVERSION) | ||
public abstract class GenericBomAstTransformation | ||
implements SpringBootAstTransformation, Ordered { | ||
|
||
private static ClassNode BOM = ClassHelper.make(DependencyManagementBom.class); | ||
|
||
@Override | ||
public void visit(ASTNode[] nodes, SourceUnit source) { | ||
for (ASTNode astNode : nodes) { | ||
if (astNode instanceof ModuleNode) { | ||
visitModule((ModuleNode) astNode, getBomModule()); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* The bom to be added to dependency management in compact form: | ||
* <code>"<groupId>:<artifactId>:<version>"</code> (like in a | ||
* <code>@Grab</code>). | ||
* | ||
* @return the maven co-ordinates of the bom to add | ||
*/ | ||
abstract protected String getBomModule(); | ||
|
||
private void visitModule(ModuleNode node, String module) { | ||
addDependencyManagementBom(node, module); | ||
} | ||
|
||
private void addDependencyManagementBom(ModuleNode node, String module) { | ||
AnnotatedNode annotated = getAnnotatedNode(node); | ||
if (annotated != null) { | ||
AnnotationNode bom = getAnnotation(annotated); | ||
List<Expression> expressions = new ArrayList<Expression>( | ||
getConstantExpressions(bom.getMember("value"))); | ||
expressions.add(new ConstantExpression(module)); | ||
bom.setMember("value", new ListExpression(expressions)); | ||
} | ||
} | ||
|
||
private AnnotationNode getAnnotation(AnnotatedNode annotated) { | ||
AnnotationNode annotation; | ||
List<AnnotationNode> annotations = annotated.getAnnotations(BOM); | ||
if (annotations.isEmpty()) { | ||
annotation = new AnnotationNode(BOM); | ||
annotated.addAnnotation(annotation); | ||
} | ||
else { | ||
annotation = annotations.get(0); | ||
} | ||
return annotation; | ||
} | ||
|
||
private AnnotatedNode getAnnotatedNode(ModuleNode node) { | ||
PackageNode pkg = node.getPackage(); | ||
if (pkg != null) { | ||
if (!pkg.getAnnotations(BOM).isEmpty()) { | ||
return pkg; | ||
} | ||
} | ||
if (!node.getClasses().isEmpty()) { | ||
ClassNode cls = node.getClasses().get(0); | ||
return cls; | ||
} | ||
return pkg; | ||
} | ||
|
||
private List<ConstantExpression> getConstantExpressions(Expression valueExpression) { | ||
if (valueExpression instanceof ListExpression) { | ||
return getConstantExpressions((ListExpression) valueExpression); | ||
} | ||
if (valueExpression instanceof ConstantExpression | ||
&& ((ConstantExpression) valueExpression).getValue() instanceof String) { | ||
return Arrays.asList((ConstantExpression) valueExpression); | ||
} | ||
return Collections.emptyList(); | ||
} | ||
|
||
private List<ConstantExpression> getConstantExpressions( | ||
ListExpression valueExpression) { | ||
List<ConstantExpression> expressions = new ArrayList<ConstantExpression>(); | ||
for (Expression expression : valueExpression.getExpressions()) { | ||
if (expression instanceof ConstantExpression | ||
&& ((ConstantExpression) expression).getValue() instanceof String) { | ||
expressions.add((ConstantExpression) expression); | ||
} | ||
} | ||
return expressions; | ||
} | ||
|
||
} |
123 changes: 123 additions & 0 deletions
123
...src/test/java/org/springframework/boot/cli/compiler/GenericBomAstTransformationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
/* | ||
* Copyright 2012-2015 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; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
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.ModuleNode; | ||
import org.codehaus.groovy.ast.PackageNode; | ||
import org.codehaus.groovy.ast.expr.ConstantExpression; | ||
import org.codehaus.groovy.ast.expr.Expression; | ||
import org.codehaus.groovy.ast.expr.ListExpression; | ||
import org.codehaus.groovy.control.SourceUnit; | ||
import org.codehaus.groovy.control.io.ReaderSource; | ||
import org.codehaus.groovy.transform.ASTTransformation; | ||
import org.junit.Test; | ||
import org.springframework.boot.groovy.DependencyManagementBom; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
|
||
/** | ||
* Tests for {@link ResolveDependencyCoordinatesTransformation} | ||
* | ||
* @author Andy Wilkinson | ||
*/ | ||
public final class GenericBomAstTransformationTests { | ||
|
||
private final SourceUnit sourceUnit = new SourceUnit((String) null, | ||
(ReaderSource) null, null, null, null); | ||
|
||
private final ModuleNode moduleNode = new ModuleNode(this.sourceUnit); | ||
|
||
private final ASTTransformation transformation = new GenericBomAstTransformation() { | ||
|
||
@Override | ||
public int getOrder() { | ||
return DependencyManagementBomTransformation.ORDER - 10; | ||
} | ||
|
||
@Override | ||
protected String getBomModule() { | ||
return "test:child:1.0.0"; | ||
} | ||
}; | ||
|
||
@Test | ||
public void transformationOfEmptyPackage() { | ||
this.moduleNode.setPackage(new PackageNode("foo")); | ||
this.transformation.visit(new ASTNode[] { this.moduleNode }, this.sourceUnit); | ||
assertEquals("[test:child:1.0.0]", getValue().toString()); | ||
} | ||
|
||
@Test | ||
public void transformationOfClass() { | ||
this.moduleNode.addClass(ClassHelper.make("MyClass")); | ||
this.transformation.visit(new ASTNode[] { this.moduleNode }, this.sourceUnit); | ||
assertEquals("[test:child:1.0.0]", getValue().toString()); | ||
} | ||
|
||
@Test | ||
public void transformationOfClassWithExistingManagedDependencies() { | ||
this.moduleNode.setPackage(new PackageNode("foo")); | ||
ClassNode cls = ClassHelper.make("MyClass"); | ||
this.moduleNode.addClass(cls); | ||
AnnotationNode annotation = new AnnotationNode( | ||
ClassHelper.make(DependencyManagementBom.class)); | ||
annotation.addMember("value", new ConstantExpression("test:parent:1.0.0")); | ||
cls.addAnnotation(annotation); | ||
this.transformation.visit(new ASTNode[] { this.moduleNode }, this.sourceUnit); | ||
assertEquals("[test:parent:1.0.0, test:child:1.0.0]", getValue().toString()); | ||
} | ||
|
||
private List<String> getValue() { | ||
Expression expression = findAnnotation().getMember("value"); | ||
if (expression instanceof ListExpression) { | ||
List<String> list = new ArrayList<String>(); | ||
for (Expression ex : ((ListExpression) expression).getExpressions()) { | ||
list.add((String) ((ConstantExpression) ex).getValue()); | ||
} | ||
return list; | ||
} | ||
else if (expression == null) { | ||
return null; | ||
} | ||
else { | ||
throw new IllegalStateException("Member 'value' is not a ListExpression"); | ||
} | ||
} | ||
|
||
private AnnotationNode findAnnotation() { | ||
PackageNode pkg = this.moduleNode.getPackage(); | ||
ClassNode bom = ClassHelper.make(DependencyManagementBom.class); | ||
if (pkg != null) { | ||
if (!pkg.getAnnotations(bom).isEmpty()) { | ||
return pkg.getAnnotations(bom).get(0); | ||
} | ||
} | ||
if (!this.moduleNode.getClasses().isEmpty()) { | ||
ClassNode cls = this.moduleNode.getClasses().get(0); | ||
return cls.getAnnotations(bom).get(0); | ||
} | ||
throw new IllegalStateException("No package or class node found"); | ||
} | ||
|
||
} |