...
| Code Block |
|---|
import org.codehaus.groovy.control.CompilerConfiguration // create a configuration def config = new CompilerConfiguration() // tweak the configuration config.addCompilationCustomizers(...) // run your script def shell = new GroovyShell(config) shell.evaluate(script) |
...
| Code Block |
|---|
import org.codehaus.groovy.control.customizers.ImportCustomizer def icz = new ImportCustomizer() // "normal" import icz.addImports('java.util.concurrent.atomic.AtomicInteger', 'java.util.concurrent.ConcurrentHashMap') // "aliases" import icz.addImport('CHM', 'java.util.concurrent.ConcurrentHashMap') // "static" import icz.addStaticImport('java.lang.Math', 'PI') // import static java.lang.Math.PIPi // "aliased static" import ica.addStaticImport('pi', 'java.lang.Math', 'PI') // import static java.lang.Math.PI as pi // "star" import icz.addStarImport 'java.util.concurrent' // import java.util.concurrent.* // "static star" import icz.addStaticStar 'java.lang.Math' // import static java.lang.Math.* |
The AST transformation customizer
...
| Code Block |
|---|
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer import groovy.transformutil.logging.Log def acz = new ASTTransformationCustomizer(Log) configconfiguration.addCompilationCustomizers(acz) |
...
| Code Block |
|---|
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("""
// equivalent to adding @ConditionalInterrupt(value={true}, thrown: SecurityException)
class MyClass {
void doIt() { }
}
new MyClass().doIt()
""")
} |
...
| Code Block |
|---|
org.codehaus.groovy.control.customizers.SecureASTCustomizer def scz = new SecureASTCustomizer() scz.with { closuresAllowed = false // user will not be able to write closures methodDefinitionAllowed = false // user will not be able to define methods importsWhitelist = [] // empty whitelist means imports are disallowed staticImportsWhitelist = [] // same for static imports staticStarImportsWhitelist = ['java.lang.Math'] // only java.lang.Math is allowed // the list of tokens the user can find // constants are defined in org.codehaus.groovy.syntax.Types tokensWhitelist = [ PLUS, MINUS, MULTIPLY, DIVIDE, MOD, POWER, PLUS_PLUS, MINUS_MINUS, COMPARE_EQUAL, COMPARE_NOT_EQUAL, COMPARE_LESS_THAN, COMPARE_LESS_THAN_EQUAL, COMPARE_GREATER_THAN, COMPARE_GREATER_THAN_EQUAL, ].asImmutable() // limit the types of constants that a user can define to number types only constantTypesClassesWhiteList = [ Integer, Float, Long, Double, BigDecimal, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE ].asImmutable() // method calls are only allowed if the receiver is of one of those types // be careful, it's not a runtime type! receiversClassesWhiteList = [ Math, Integer, Float, Double, Long, BigDecimal ].asImmutable() } |
...
| Code Block |
|---|
import org.codehaus.groovy.control.customizers.SourceAwareCustomizer
import org.codehaus.groovy.control.customizers.ImportCustomizer
def delegate = new ImportCustomizer()
def sac = new SourceAwareCustomizer(delegate) |
...
| Code Block |
|---|
// the customizer will only be applied to classes ending with 'Bean'
sac.baseNameValidator = { baseName ->
baseName.endsWith 'Bean'
}
// the customizer will only be applied to files which extension is '.spec'
sac.extensionValidator = { ext -> ext == 'spec' }
// source unit validation
sac.sourceUnitValidator = { sourceUnit -> ... } |
...
| Code Block |
|---|
import org.codehaus.groovy.control.CompilerConfiguration
import static org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder.withConfig
def conf = new CompilerConfiguration()
withConfig(conf) {
...
} |
...
Imports
| Code Block |
|---|
withConfig(confconfiguration) { imports { // imports customizer normal 'my.package.MyClass' // a normal import alias 'AI', 'java.util.concurrent.atomic.AtomicInteger' // an aliased import star 'java.util.concurrent' // star imports staticMember 'java.lang.Math', 'PI' // static import staticMember 'pi', 'java.lang.Math', 'PI' // aliased static import } } |
...
| Code Block |
|---|
// apply @Log transparently
withConfig(conf) {
ast(Log)
}
// apply @Log, with a different logger name
withConfig(conf) {
ast(Log, value: 'LOGGER')
} |
...
| Code Block |
|---|
// apply CompileStatic AST annotation on .sgroovy files withConfig(confconfiguration)(extension: 'sgroovy') { ast(CompileStatic) } // apply CompileStatic AST annotation on .sgroovy or .sg files withConfig(confconfiguration)(extensions: ['sgroovy','sg']) { ast(CompileStatic) } // apply CompileStatic AST annotation on .sgroovy or .sg files withConfig(confconfiguration)(extensionValidator: { it.name in ['sgroovy','sg']}) { ast(CompileStatic) } // apply CompileStatic AST annotation on files whose name is 'foo' withConfig(confconfiguration)(basename: 'foo') { ast(CompileStatic) } // apply CompileStatic AST annotation on files whose name is 'foo' or 'bar' withConfig(confconfiguration)(basenames: ['foo', 'bar']) { ast(CompileStatic) } // apply CompileStatic AST annotation on files whose name is 'foo' or 'bar' withConfig(confconfiguration)(basenames: { it in ['foo', 'bar'] }) { ast(CompileStatic) } // apply CompileStatic AST annotation on files that do not contain a class named 'Baz' withConfig(confconfiguration)(unitValidator: { unit -> !unit.AST.classes.any { it.name == 'Baz' } }) { ast(CompileStatic) } |
...
Inlined customizer allows you to write a compilation customizer directly, without having to create a dedicated class for it.
| Code Block |
|---|
withConfig(confconfiguration) { inline(phase:'CONVERSION') { source, context, classNode -> println "visiting $classNode" } } |
...
Of course, the builder allows you to define multiple customizers at once:
| Code Block |
|---|
withConfig(confconfiguration) { ast(ToString) ast(EqualsAndHashCode) } |
...