From a6a19292745d2e70aee12feed6660ef0a14840d2 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 29 Oct 2013 00:02:04 -0700 Subject: [PATCH] Extract inner-classes from GroovyCompiler Extract and refactor inner-classes from the GroovyCompiler to improve code readability. --- ...ndencyAutoConfigurationTransformation.java | 73 ++++++ .../cli/compiler/DependencyCustomizer.java | 18 +- .../boot/cli/compiler/GroovyCompiler.java | 236 +++++------------- ...veDependencyCoordinatesTransformation.java | 141 +++++++++++ 4 files changed, 279 insertions(+), 189 deletions(-) create mode 100644 spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/DependencyAutoConfigurationTransformation.java create mode 100644 spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/ResolveDependencyCoordinatesTransformation.java diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/DependencyAutoConfigurationTransformation.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/DependencyAutoConfigurationTransformation.java new file mode 100644 index 00000000000..8d62ac5a3de --- /dev/null +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/DependencyAutoConfigurationTransformation.java @@ -0,0 +1,73 @@ +/* + * 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; + +import groovy.lang.GroovyClassLoader; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.transform.ASTTransformation; + +/** + * {@link ASTTransformation} to apply + * {@link CompilerAutoConfiguration#applyDependencies(DependencyCustomizer) dependency + * auto-configuration}. + * + * @author Phillip Webb + * @author Dave Syer + * @author Andy Wilkinson + */ +class DependencyAutoConfigurationTransformation implements ASTTransformation { + + private final GroovyClassLoader loader; + + private final ArtifactCoordinatesResolver coordinatesResolver; + + private final Iterable compilerAutoConfigurations; + + DependencyAutoConfigurationTransformation(GroovyClassLoader loader, + ArtifactCoordinatesResolver coordinatesResolver, + Iterable compilerAutoConfigurations) { + this.loader = loader; + this.coordinatesResolver = coordinatesResolver; + this.compilerAutoConfigurations = compilerAutoConfigurations; + + } + + @Override + public void visit(ASTNode[] nodes, SourceUnit source) { + for (ASTNode astNode : nodes) { + if (astNode instanceof ModuleNode) { + visitModule((ModuleNode) astNode); + } + } + } + + private void visitModule(ModuleNode module) { + DependencyCustomizer dependencies = new DependencyCustomizer(this.loader, module, + this.coordinatesResolver); + for (ClassNode classNode : module.getClasses()) { + for (CompilerAutoConfiguration autoConfiguration : this.compilerAutoConfigurations) { + if (autoConfiguration.matches(classNode)) { + autoConfiguration.applyDependencies(dependencies); + } + } + } + } +} diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/DependencyCustomizer.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/DependencyCustomizer.java index a7e652de7a9..e2b43d2884c 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/DependencyCustomizer.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/DependencyCustomizer.java @@ -41,17 +41,17 @@ public class DependencyCustomizer { private final ClassNode classNode; - private final ArtifactCoordinatesResolver artifactCoordinatesResolver; + private final ArtifactCoordinatesResolver coordinatesResolver; /** * Create a new {@link DependencyCustomizer} instance. * @param loader */ public DependencyCustomizer(GroovyClassLoader loader, ModuleNode moduleNode, - ArtifactCoordinatesResolver artifactCoordinatesResolver) { + ArtifactCoordinatesResolver coordinatesResolver) { this.loader = loader; this.classNode = moduleNode.getClasses().get(0); - this.artifactCoordinatesResolver = artifactCoordinatesResolver; + this.coordinatesResolver = coordinatesResolver; } /** @@ -61,7 +61,7 @@ public class DependencyCustomizer { protected DependencyCustomizer(DependencyCustomizer parent) { this.loader = parent.loader; this.classNode = parent.classNode; - this.artifactCoordinatesResolver = parent.artifactCoordinatesResolver; + this.coordinatesResolver = parent.coordinatesResolver; } public String getVersion(String artifactId) { @@ -70,7 +70,7 @@ public class DependencyCustomizer { } public String getVersion(String artifactId, String defaultVersion) { - String version = this.artifactCoordinatesResolver.getVersion(artifactId); + String version = this.coordinatesResolver.getVersion(artifactId); if (version == null) { version = defaultVersion; } @@ -184,8 +184,8 @@ public class DependencyCustomizer { * @return this {@link DependencyCustomizer} for continued use */ public DependencyCustomizer add(String module) { - return this.add(this.artifactCoordinatesResolver.getGroupId(module), module, - this.artifactCoordinatesResolver.getVersion(module), true); + return this.add(this.coordinatesResolver.getGroupId(module), module, + this.coordinatesResolver.getVersion(module), true); } /** @@ -198,8 +198,8 @@ public class DependencyCustomizer { * @return this {@link DependencyCustomizer} for continued use */ public DependencyCustomizer add(String module, boolean transitive) { - return this.add(this.artifactCoordinatesResolver.getGroupId(module), module, - this.artifactCoordinatesResolver.getVersion(module), transitive); + return this.add(this.coordinatesResolver.getGroupId(module), module, + this.coordinatesResolver.getVersion(module), transitive); } private DependencyCustomizer add(String group, String module, String version, diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java index bc044682729..e55ffd6aa0f 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java @@ -17,7 +17,6 @@ package org.springframework.boot.cli.compiler; import groovy.grape.GrapeEngine; -import groovy.lang.Grab; import groovy.lang.GroovyClassLoader; import groovy.lang.GroovyClassLoader.ClassCollector; @@ -34,14 +33,7 @@ import java.util.List; import java.util.ServiceLoader; import org.codehaus.groovy.ast.ASTNode; -import org.codehaus.groovy.ast.AnnotatedNode; -import org.codehaus.groovy.ast.AnnotationNode; -import org.codehaus.groovy.ast.ClassCodeVisitorSupport; import org.codehaus.groovy.ast.ClassNode; -import org.codehaus.groovy.ast.ImportNode; -import org.codehaus.groovy.ast.ModuleNode; -import org.codehaus.groovy.ast.expr.ConstantExpression; -import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.classgen.GeneratorContext; import org.codehaus.groovy.control.CompilationFailedException; import org.codehaus.groovy.control.CompilationUnit; @@ -66,6 +58,7 @@ import org.codehaus.groovy.transform.ASTTransformationVisitor; * *
  • Generated class files can also be loaded using * {@link ClassLoader#getResource(String)}
  • + * *
      * * @author Phillip Webb @@ -77,32 +70,45 @@ public class GroovyCompiler { private static final ClassLoader AETHER_CLASS_LOADER = new URLClassLoader( new URL[] { GroovyCompiler.class.getResource("/internal/") }); - private GroovyCompilerConfiguration configuration; + private final GroovyCompilerConfiguration configuration; - private ExtendedGroovyClassLoader loader; + private final ExtendedGroovyClassLoader loader; - private ArtifactCoordinatesResolver artifactCoordinatesResolver; + private final ArtifactCoordinatesResolver coordinatesResolver; - private final ASTTransformation dependencyCustomizerTransformation = new DependencyCustomizerAstTransformation(); + private final ServiceLoader compilerAutoConfigurations; - private final ASTTransformation dependencyCoordinatesTransformation = new DefaultDependencyCoordinatesAstTransformation(); + private final List transformations; /** * Create a new {@link GroovyCompiler} instance. * @param configuration the compiler configuration */ - @SuppressWarnings("unchecked") public GroovyCompiler(final GroovyCompilerConfiguration configuration) { this.configuration = configuration; - CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); this.loader = new ExtendedGroovyClassLoader(getClass().getClassLoader(), - compilerConfiguration); + new CompilerConfiguration()); if (configuration.getClasspath().length() > 0) { this.loader.addClasspath(configuration.getClasspath()); } - this.artifactCoordinatesResolver = new PropertiesArtifactCoordinatesResolver( - this.loader); + this.coordinatesResolver = new PropertiesArtifactCoordinatesResolver(this.loader); + installGrapeEngine(); + this.loader.getConfiguration().addCompilationCustomizers( + new CompilerAutoConfigureCustomizer()); + this.compilerAutoConfigurations = ServiceLoader.load( + CompilerAutoConfiguration.class, GroovyCompiler.class.getClassLoader()); + this.transformations = new ArrayList(); + this.transformations.add(new DependencyAutoConfigurationTransformation(this.loader, + this.coordinatesResolver, this.compilerAutoConfigurations)); + if (this.configuration.isGuessDependencies()) { + this.transformations.add(new ResolveDependencyCoordinatesTransformation( + this.coordinatesResolver)); + } + } + + @SuppressWarnings("unchecked") + private void installGrapeEngine() { try { Class grapeEngineClass = (Class) AETHER_CLASS_LOADER .loadClass("org.springframework.boot.cli.compiler.AetherGrapeEngine"); @@ -110,16 +116,13 @@ public class GroovyCompiler { GroovyClassLoader.class, String.class, String.class, String.class); GrapeEngine grapeEngine = constructor.newInstance(this.loader, "org.springframework.boot", "spring-boot-starter-parent", - this.artifactCoordinatesResolver.getVersion("spring-boot")); + this.coordinatesResolver.getVersion("spring-boot")); new GrapeEngineInstaller(grapeEngine).install(); } catch (Exception ex) { throw new IllegalStateException("Failed to install custom GrapeEngine", ex); } - - compilerConfiguration - .addCompilationCustomizers(new CompilerAutoConfigureCustomizer()); } public void addCompilationCustomizers(CompilationCustomizer... customizers) { @@ -157,12 +160,12 @@ public class GroovyCompiler { this.loader.clearCache(); List> classes = new ArrayList>(); - CompilerConfiguration compilerConfiguration = this.loader.getConfiguration(); + CompilerConfiguration configuration = this.loader.getConfiguration(); - final CompilationUnit compilationUnit = new CompilationUnit( - compilerConfiguration, null, this.loader); - SourceUnit sourceUnit = new SourceUnit(file[0], compilerConfiguration, - this.loader, compilationUnit.getErrorCollector()); + CompilationUnit compilationUnit = new CompilationUnit(configuration, null, + this.loader); + SourceUnit sourceUnit = new SourceUnit(file[0], configuration, this.loader, + compilationUnit.getErrorCollector()); ClassCollector collector = this.loader.createCollector(compilationUnit, sourceUnit); compilationUnit.setClassgenCallback(collector); @@ -189,18 +192,23 @@ public class GroovyCompiler { } return classes.toArray(new Class[classes.size()]); - } @SuppressWarnings("rawtypes") - private void addAstTransformations(final CompilationUnit compilationUnit) { + private void addAstTransformations(CompilationUnit compilationUnit) { + LinkedList[] phaseOperations = getPhaseOperations(compilationUnit); + processConversionOperations(phaseOperations[Phases.CONVERSION]); + } + + @SuppressWarnings("rawtypes") + private LinkedList[] getPhaseOperations(final CompilationUnit compilationUnit) { try { Field field = CompilationUnit.class.getDeclaredField("phaseOperations"); field.setAccessible(true); LinkedList[] phaseOperations = (LinkedList[]) field.get(compilationUnit); - processConversionOperations(phaseOperations[Phases.CONVERSION]); + return phaseOperations; } - catch (Exception npe) { + catch (Exception ex) { throw new IllegalStateException( "Phase operations not available from compilation unit"); } @@ -208,24 +216,26 @@ public class GroovyCompiler { @SuppressWarnings({ "rawtypes", "unchecked" }) private void processConversionOperations(LinkedList conversionOperations) { - for (int i = 0; i < conversionOperations.size(); i++) { - Object operation = conversionOperations.get(i); + int index = getIndexOfASTTransformationVisitor(conversionOperations); + conversionOperations.add(index, new CompilationUnit.SourceUnitOperation() { + @Override + public void call(SourceUnit source) throws CompilationFailedException { + ASTNode[] nodes = new ASTNode[] { source.getAST() }; + for (ASTTransformation transformation : GroovyCompiler.this.transformations) { + transformation.visit(nodes, source); + } + } + }); + } - if (operation.getClass().getName() + private int getIndexOfASTTransformationVisitor(LinkedList conversionOperations) { + for (int index = 0; index < conversionOperations.size(); index++) { + if (conversionOperations.get(index).getClass().getName() .startsWith(ASTTransformationVisitor.class.getName())) { - conversionOperations.add(i, new CompilationUnit.SourceUnitOperation() { - @Override - public void call(SourceUnit source) throws CompilationFailedException { - ASTNode[] astNodes = new ASTNode[] { source.getAST() }; - GroovyCompiler.this.dependencyCustomizerTransformation.visit( - astNodes, source); - GroovyCompiler.this.dependencyCoordinatesTransformation.visit( - astNodes, source); - } - }); - break; + return index; } } + return conversionOperations.size(); } /** @@ -243,12 +253,8 @@ public class GroovyCompiler { ImportCustomizer importCustomizer = new ImportCustomizer(); - ServiceLoader customizers = ServiceLoader.load( - CompilerAutoConfiguration.class, - GroovyCompiler.class.getClassLoader()); - // Additional auto configuration - for (CompilerAutoConfiguration autoConfiguration : customizers) { + for (CompilerAutoConfiguration autoConfiguration : GroovyCompiler.this.compilerAutoConfigurations) { if (autoConfiguration.matches(classNode)) { if (GroovyCompiler.this.configuration.isGuessImports()) { autoConfiguration.applyImports(importCustomizer); @@ -270,134 +276,4 @@ public class GroovyCompiler { } } - - private class DependencyCustomizerAstTransformation implements ASTTransformation { - - @Override - public void visit(ASTNode[] nodes, SourceUnit source) { - - ServiceLoader customizers = ServiceLoader.load( - CompilerAutoConfiguration.class, - GroovyCompiler.class.getClassLoader()); - - for (ASTNode astNode : nodes) { - if (astNode instanceof ModuleNode) { - ModuleNode module = (ModuleNode) astNode; - - DependencyCustomizer dependencyCustomizer = new DependencyCustomizer( - GroovyCompiler.this.loader, module, - GroovyCompiler.this.artifactCoordinatesResolver); - - ClassNode firstClass = null; - - for (ClassNode classNode : module.getClasses()) { - if (firstClass == null) { - firstClass = classNode; - } - for (CompilerAutoConfiguration autoConfiguration : customizers) { - if (autoConfiguration.matches(classNode)) { - if (GroovyCompiler.this.configuration - .isGuessDependencies()) { - autoConfiguration - .applyDependencies(dependencyCustomizer); - } - } - } - } - } - } - } - } - - private class DefaultDependencyCoordinatesAstTransformation implements - ASTTransformation { - - @Override - public void visit(ASTNode[] nodes, final SourceUnit source) { - for (ASTNode node : nodes) { - if (node instanceof ModuleNode) { - ModuleNode module = (ModuleNode) node; - for (ImportNode importNode : module.getImports()) { - visitAnnotatedNode(importNode); - } - for (ClassNode classNode : module.getClasses()) { - visitAnnotatedNode(classNode); - classNode.visitContents(new ClassCodeVisitorSupport() { - @Override - protected SourceUnit getSourceUnit() { - return source; - } - - @Override - public void visitAnnotations(AnnotatedNode node) { - visitAnnotatedNode(node); - } - }); - } - } - } - } - - private void visitAnnotatedNode(AnnotatedNode annotatedNode) { - for (AnnotationNode annotationNode : annotatedNode.getAnnotations()) { - if (isGrabAnnotation(annotationNode)) { - transformGrabAnnotation(annotationNode); - } - } - } - - private boolean isGrabAnnotation(AnnotationNode annotationNode) { - String annotationClassName = annotationNode.getClassNode().getName(); - return annotationClassName.equals(Grab.class.getName()) - || annotationClassName.equals(Grab.class.getSimpleName()); - } - - private void transformGrabAnnotation(AnnotationNode grabAnnotation) { - grabAnnotation.setMember("initClass", new ConstantExpression(false)); - - Expression valueExpression = grabAnnotation.getMember("value"); - String value = null; - if (valueExpression instanceof ConstantExpression) { - ConstantExpression constantExpression = (ConstantExpression) valueExpression; - Object valueObject = constantExpression.getValue(); - if (valueObject instanceof String) { - value = (String) valueObject; - if (isConvenienceForm(value)) { - return; - } - } - } - - applyGroupAndVersion(grabAnnotation, value); - } - - private boolean isConvenienceForm(String value) { - return value.contains(":") || value.contains("#"); - } - - private void applyGroupAndVersion(AnnotationNode grabAnnotation, String module) { - - if (module != null) { - grabAnnotation.setMember("module", new ConstantExpression(module)); - } - else { - module = (String) ((ConstantExpression) grabAnnotation.getMembers().get( - "module")).getValue(); - } - - if (grabAnnotation.getMember("group") == null) { - ConstantExpression groupIdExpression = new ConstantExpression( - GroovyCompiler.this.artifactCoordinatesResolver - .getGroupId(module)); - grabAnnotation.setMember("group", groupIdExpression); - } - - if (grabAnnotation.getMember("version") == null) { - ConstantExpression versionExpression = new ConstantExpression( - GroovyCompiler.this.artifactCoordinatesResolver - .getVersion(module)); - grabAnnotation.setMember("version", versionExpression); - } - } - } } diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/ResolveDependencyCoordinatesTransformation.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/ResolveDependencyCoordinatesTransformation.java new file mode 100644 index 00000000000..d3f936b070c --- /dev/null +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/ResolveDependencyCoordinatesTransformation.java @@ -0,0 +1,141 @@ +/* + * 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; + +import groovy.lang.Grab; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ImportNode; +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.transform.ASTTransformation; + +/** + * {@link ASTTransformation} to resolve {@link Grab} artifact coordinates. + * @author Andy Wilkinson + * @author Phillip Webb + */ +class ResolveDependencyCoordinatesTransformation implements ASTTransformation { + + private static final Set GRAB_ANNOTATION_NAMES = Collections + .unmodifiableSet(new HashSet(Arrays.asList(Grab.class.getName(), + Grab.class.getSimpleName()))); + + private final ArtifactCoordinatesResolver coordinatesResolver; + + ResolveDependencyCoordinatesTransformation( + ArtifactCoordinatesResolver coordinatesResolver) { + this.coordinatesResolver = coordinatesResolver; + } + + @Override + public void visit(ASTNode[] nodes, SourceUnit source) { + ClassVisitor classVisitor = new ClassVisitor(source); + for (ASTNode node : nodes) { + if (node instanceof ModuleNode) { + ModuleNode module = (ModuleNode) node; + for (ImportNode importNode : module.getImports()) { + visitAnnotatedNode(importNode); + } + for (ClassNode classNode : module.getClasses()) { + visitAnnotatedNode(classNode); + classNode.visitContents(classVisitor); + } + } + } + } + + private void visitAnnotatedNode(AnnotatedNode annotatedNode) { + for (AnnotationNode annotationNode : annotatedNode.getAnnotations()) { + if (GRAB_ANNOTATION_NAMES.contains(annotationNode.getClassNode().getName())) { + transformGrabAnnotation(annotationNode); + } + } + } + + private void transformGrabAnnotation(AnnotationNode grabAnnotation) { + grabAnnotation.setMember("initClass", new ConstantExpression(false)); + String value = getValue(grabAnnotation); + if (value != null && !isConvenienceForm(value)) { + applyGroupAndVersion(grabAnnotation, value); + } + } + + private String getValue(AnnotationNode annotation) { + Expression expression = annotation.getMember("value"); + if (expression instanceof ConstantExpression) { + Object value = ((ConstantExpression) expression).getValue(); + return (value instanceof String ? (String) value : null); + } + return null; + } + + private boolean isConvenienceForm(String value) { + return value.contains(":") || value.contains("#"); + } + + private void applyGroupAndVersion(AnnotationNode annotation, String module) { + if (module != null) { + setMember(annotation, "module", module); + } + else { + Expression expression = annotation.getMembers().get("module"); + module = (String) ((ConstantExpression) expression).getValue(); + } + if (annotation.getMember("group") == null) { + setMember(annotation, "group", this.coordinatesResolver.getGroupId(module)); + } + if (annotation.getMember("version") == null) { + setMember(annotation, "version", this.coordinatesResolver.getVersion(module)); + } + } + + private void setMember(AnnotationNode annotation, String name, String value) { + ConstantExpression expression = new ConstantExpression(value); + annotation.setMember(name, expression); + } + + private class ClassVisitor extends ClassCodeVisitorSupport { + + private final SourceUnit source; + + public ClassVisitor(SourceUnit source) { + this.source = source; + } + + @Override + protected SourceUnit getSourceUnit() { + return this.source; + } + + @Override + public void visitAnnotations(AnnotatedNode node) { + visitAnnotatedNode(node); + } + } +}