Asteroid is a set of utilities to make it easier to develop Groovy AST transformations.
1. What is Asteroid
AST transformations, have been historically a hard topic in Groovy. Asteroid is a set of utilities and ideas trying to reduce the complexity of dealing with transformations.
If you have never done any AST transformation I’d recommend you to take a look both the Groovy documentation and the theory chapter. If you already now the stuff then read on.
| At the moment Asteroid development is in an alpha state. Please check the changelog file to follow the progress of the project. | 
2. Show me the code
2.1. Gradle
In order to use Asteroid in your Groovy project just add the jcenter
repository:
repositories {
    jcenter()
}Then you can add the dependency to your project:
compile 'com.github.grooviter:asteroid:0.5.0'2.2. Example
To show the benefit of using Asteroid, I will be following the
tutorial about local transformation available at the
Groovy
official site. The code of the following example is available at the
asteroid-test module at
Github.
Given a code like the following:
@WithLogging
def greet() {
    println "Hello World"
}
greet()We would like to print a start and stop message along with the message printed by the method itself. So in this example we’ll be expecting an output like:
start greet
Hello World
stop greetFor a local transformation only two things are required:
- 
The annotation used as a marker. In this example the @WithLoggingannotation
- 
The transformation implementation 
Lets see first the @WithLogging annotation declaration:
package asteroid.local.samples
import asteroid.Local
@Local(
    value   = WithLoggingTransformationImpl, (1)
    applyTo = Local.TO.METHOD)               (2)
@interface WithLogging { }| 1 | The transformation implementation class | 
| 2 | This annotation will be applied to method elements. | 
| By default @Localannotation assumes the annotation is applied to atype(classes). So if you are using the annotation for a type then you could omitvalueandapplyToattributes an write just the class of the transformation like this:@Local(ImplementationClass). | 
Now it’s time to implement the transformation. The transformation
should be an instance of
asteroid.local.AbstractLocalTransformation. We have to extend
AbstractLocalTransformation and provide two generic arguments:
- 
The annotation class used to mark our transformation: WithLogging
- 
The type of nodes that will be affected by this transformation. In this example org.codehaus.groovy.ast.MethodNode
package asteroid.local.samples
import asteroid.A
import asteroid.Phase
import asteroid.AbstractLocalTransformation
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.AnnotationNode
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.ast.stmt.Statement
@CompileStatic
@Phase(Phase.LOCAL.SEMANTIC_ANALYSIS) (1)
class WithLoggingTransformationImpl extends AbstractLocalTransformation<WithLogging, MethodNode> {
    @Override
    void doVisit(final AnnotationNode annotation, final MethodNode methodNode) {
        def before = printlnS("start") (2)
        def after = printlnS("end")   (3)
        A.UTIL.NODE.addAroundCodeBlock(methodNode, before, after) (4)
    }
    Statement printlnS(String message) {
        return A.STMT.stmt(A.EXPR.callThisX("println", A.EXPR.constX(message))) (5)
    }
}| 1 | The @Phaseannotation indicates in which compilation phase this transformation will be applied. | 
| 2 | Building println "start"code, which is wrapped in aorg.codehaus.groovy.ast.stmt.Statement | 
| 3 | Building println "end"code, which is wrapped in aorg.codehaus.groovy.ast.stmt.Statement | 
| 4 | Building the new method code re-arranging the new and old code in order. | 
| 5 | Building a generic println constantValueexpression | 
The @CompileStatic annotation is not required it’s only used here to highlight that all the code used in this
transformation is safely typed and can be optimized by this annotation.
3. Overview
At the moment Asteroid is composed by two main groups, abstractions to reduce the complexity of creating a new transformation, and utility classes helping to create new Abstract Syntaxt Tree nodes.
 
3.1. Transform abstractions
So far abstractions used to deal with the AST were too low level. For instance, you needed to check whether the nodes passed to your transformation were the ones you wanted to act over or not, and then proceed.
Asteroid tries to provide higher abstractions in order to reduce some of the boiler plate code, and make the developer to focus on the transformation only.
3.2. AST nodes functions
The other main part of Asteroid are functions dealing directly with AST nodes. Functions responsible for modifying AST nodes.
They’re divided in four groups:
- 
Expressions: Functions responsible for creating expressions 
- 
Statements: Functions responsible for creating statements 
- 
Nodes: Builders responsible for creating high level nodes 
- 
Utils: Functions responsible for querying and querying any type of nodes 
| With the upcoming groovy-macromodule in Groovy 2.5.0 most
of the code in Asteroid, used for creatingexpressionsandstatementsmay be gone for good in favor of themacromethod. | 
3.2.1. The A class
All functions available in Asteroid are accessible through the
asteroid.A class.
Check javadoc: asteroid.A
4. Theory
What can you do with an AST transformation ?
- 
Transform the Abstract Syntax Tree by adding, or removing elements from it 
- 
Check the structure, or semantics of the Abstract Syntax Tree and do something about it 
Examples of adding / removing
- 
Groovy: Code generation transformations: @ToString, @Immutable… 
- 
Spock: transforms label statements 
- 
Swissknife: reduces boilerplate code in Android dev 
- 
Grails: also saves you from typical web boilerplate code 
 
  
  
 
Examples of checking
- 
GContracts: programming by contract 
- 
Codenarc: Static Analysis 

Transformations can be of two types:
Local
- 
Relative to the context they are applied to. 
- 
That context is marked (annotation) 
- 
Compilation phases are limited 
Global
- 
Global AST transformations are applied to all source code 
- 
Compilation phases are less limited 
- 
Need an extra descriptor file 
4.1. AST
Abstract Syntax Tree (or AST from now on) is the tree-like representation of the code the compiler needs in order to generate the bytecode that will be used later by the JVM.
 
When dealing with the AST, most of the time we will talking about three types of elements:
- 
EXPRESSIONS 
- 
STATEMENTS 
- 
HIGH LEVEL NODES 
 
4.1.1. Expressions
An expression is a combination of one or more explicit values, constants, variables, operators, and functions that the programming language interprets and computes to produce another value.
1 == 1 
- 
constant expression 1 
- 
token == 
- 
constant expression 1 
ref.myMethod(3) 
- 
variable expression ref 
- 
constant myMethod 
- 
param expression 3 
4.1.2. Statements
In computer programming, a statement is the smallest standalone element of an imperative programming language that expresses some action to be carried out. A statement may have expressions.
if(booleanExpression) {
 println "hello" // statement
}- 
expression to evaluate 
- 
statement to be executed if the boolean expression evaluates to true 
public void main(String[] args) { // block starts
  // this is inside a block statement
} // block ends- 
A block statement is easily recognized by curly braces 
- 
It is built from other statements containing expressions 
public String greetings() {
    return "Hello Greach"
}This block statement contains a return statement receiving a constant expression Hello Greach.
4.1.3. High level Nodes
Is how our program is structured. They group statements and expressions:
- 
classes 
- 
methods 
- 
fields 
- 
properties 
- 
… 
class A { // ClassNode
   String greetings // FieldNode
   String hello() { // MethodNode
   }
}- 
ClassNode may contain: methods, fields… 
- 
MethodNode may contain statements, and expressions 
- 
… 
Therefore…
class A { // ClassNode
   String hello() // MethodNode
   { // blockStatement {
       return "Hello" // returnStatement(constantExpression)
    } // }
}5. Local Transformations
Local AST transformations are relative to the context they are applied to. In most cases, the context is defined by an annotation that will define the scope of the transform. For example, annotating a field would mean that the transformation applies to the field, while annotating the class would mean that the transformation applies to the whole class.
5.1. Overview
In order to create a local transformation you need to:
- 
Create an annotationannotated by@Local
- 
Create an implementationof the transformation extendingAbstractLocalTransformation
- 
Your implementation should be annotated by @Phasewith the proper local compilation phase value set.
5.2. @Local
In a local transformation you normally use an annotation to mark those parts of the code you want to transform: classes, methods… That annotation should be annotated as well to tell the compiler that is going to be used as a transformation marker.
You can use @Local to annotate a marker annotation. The only
mandatory argument is the AST implementation class. Implementation
classes should always extend
asteroid.local.AbstractLocalTransformation class.
package asteroid.local.samples
import asteroid.Local
@Local(AsListImpl)
@interface AsList { }If @Local annotation does not indicate which type of element is
allowed to annotate by the attribute appliedTo then is supposed to
be used over an element of type TYPE, meaning it will be applied
over an entire class.
Underneath the @Local annotation is doing:
 
5.3. applyTo
applyTo attribute is used when the transformation is applied to any
element type other than TYPE: a method, annotation, field…etc.
package asteroid.local.samples
import asteroid.Local
@Local(
    value   = WithLoggingTransformationImpl, (1)
    applyTo = Local.TO.METHOD)               (2)
@interface WithLogging { }| 1 | This annotation will be applied to method elements. | 
| 2 | The class of the AST transformation implementation | 
Sometimes you may want to target more than one element: a class and
a method, or a parameter and a local variable. Then you can use
TO.ANNOTATED that means that the transformation could be applied
to any annotated node.
package asteroid.local.samples
import asteroid.Local
@Local(value = NameCheckerImpl, applyTo = Local.TO.ANNOTATED)
@interface NameChecker {
    String value()
}| Most of the times a single node type is targeted. That
idea was kept in mind when creating the applyToparameter. That’s
whyapplyTodoesn’t receive a list of possible targets.ANNOTATEDis the exception to the rule that allows to do so. | 
Therefore in your implementation you should
use the AnnotatedNode type.
| The following example of method overriding only works with dynamic Groovy, if
you statically compiled the code it would only be executing getName(AnnotatedNode). So
if you compile your code statically you should probably be using ainstanceofto
dispatch your actions. | 
package asteroid.local.samples
import asteroid.A
import asteroid.AbstractLocalTransformation
import asteroid.Phase
import org.codehaus.groovy.ast.AnnotatedNode
import org.codehaus.groovy.ast.AnnotationNode
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.FieldNode
/**
 * Checks whether an {@link AnnotatedNode} follows the defined pattern in the
 * pattern property
 *
 * @since 0.2.5
 */
@Phase(Phase.LOCAL.INSTRUCTION_SELECTION)
class NameCheckerImpl extends AbstractLocalTransformation<NameChecker, AnnotatedNode> { (1)
    @Override
    void doVisit(AnnotationNode annotation, AnnotatedNode annotated) { (2)
        String pattern = A.UTIL.NODE.getStringValue(annotation)
        String nodeText = getName(annotated)
        Boolean matches = nodeText ==~ pattern
        if (!matches) {
            addError 'Pattern doesn\'t match annotated name', annotated
        }
    }
    String getName(ClassNode classNode){  (3)
        return classNode.name
    }
    String getName(FieldNode fieldNode) { (4)
        return fieldNode.name
    }
    String getName(AnnotatedNode annotatedNode) { (5)
        addError "Pattern doesn't match annotated name", annotatedNode
    }
}| 1 | You expect to receive any type of node extending AnnotatedNode | 
| 2 | Receiving the annotated node as a parameter | 
| 3 | Executing logic if the node is of type ClassNode | 
| 4 | Executing logic if the node is of type FieldNode | 
| 5 | Executing logic if the node is of any other type extending AnnotatedNode | 
Then you can use it in any annotated node:
package asteroid.local.samples
@NameChecker('.*Subject')
class CheckerSubject {
  @NameChecker('.*Field')
  String stringField = 'doSomething'
}
assert new CheckerSubject().stringField == 'doSomething'5.4. AbstractLocalTransformation
asteroid.local.AbstractLocalTransformation exists to avoid some of the
defensive code that you would normally write at the beggining of an
AST transformation.
When coding an AST transformation you always check that the first node
is an AnnotationNode and the second is the type of ASTNode you
expected to be annotated by the first node. Instead of coding that you
can use AbstractLocalTransformation.
Lets say I have an annotation @ToMD5. That annotation can only be
used in elements of type FIELD:
package asteroid.local.samples
import asteroid.Local
@Local(value = ToMD5Impl, applyTo = Local.TO.FIELD)
@interface ToMD5 { }I would like to create a method for every field annotated by ToMD5
returning the MD5 signature of the content of that field.
In order to implement that I’m using AbstractLocalTransformation:
package asteroid.local.samples
import asteroid.A
import asteroid.Phase
import asteroid.AbstractLocalTransformation
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.AnnotationNode
import org.codehaus.groovy.ast.FieldNode
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.ast.stmt.BlockStatement
@CompileStatic
@Phase(Phase.LOCAL.SEMANTIC_ANALYSIS) (1)
class ToMD5Impl extends AbstractLocalTransformation<ToMD5, FieldNode> { (2)
    @Override
    void doVisit(AnnotationNode annotation, FieldNode node) { (3)
        BlockStatement block  = buildMethodCode(node.name)
        MethodNode methodNode = A.NODES.method("${node.name}ToMD5")
            .modifiers(A.ACC.ACC_PUBLIC)
            .returnType(String)
            .code(block)
            .build()
        A.UTIL.NODE.addMethod(node.declaringClass, methodNode)
    }
    private BlockStatement buildMethodCode(final String name) {
        A.STMT.blockSFromString """
            return java.security.MessageDigest
                .getInstance('MD5')
                .digest(${name}.getBytes())
                .encodeHex()
                .toString()
        """
    }
}| 1 | Declaring when to apply this transformation with the annotation @Phaseand the correspondent compilation phase. | 
| 2 | Creating a class extending AbstractLocalTransformationand declaring
that the annotation and the affected node type areToMD5andFieldNoderespectively | 
| 3 | The override method declares the correct generic type FieldNode. | 
From this line on you don’t have to be worried about casting first and second node passed to your transformation anymore.
| Sometimes it comes handy to get a reference to org.codehaus.groovy.control.SourceUnit. In previous versionsSourceUnitwas passed as argument, but it forced to add an import
whether you used or not. Now it’s present as a class field. Probably
in future release won’t be available directly but through specific
functions. | 
5.5. @Phase
@Phase is a required annotation for both global and local
transformations that indicates in which compilation phase this
transformation will be applied.
Lets see how @Phase annotation is processed in a local transformation:
 
@Phase annotation needs a value of type
org.codehaus.groovy.control.CompilePhase enum, but because sometimes
is hard to remember which phases are available depending on which type
of transformation we are implementing and it would add one more import
to our code, Asteroid provides a shortcut to these values:
- 
asteroid.Phase.LOCAL
- 
asteroid.Phase.GLOBAL
This way is always easier to remember how to get the proper compilation phase. Here’s an example:
package asteroid.local.samples
import asteroid.A
import asteroid.Phase
import asteroid.AbstractLocalTransformation
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.AnnotationNode
@Phase(Phase.LOCAL.SEMANTIC_ANALYSIS) (1)
class AsListImpl extends AbstractLocalTransformation<AsList, ClassNode> {
    @Override
    void doVisit(AnnotationNode annotation, ClassNode classNode) {
        classNode.superClass = A.NODES.clazz(ArrayList).build()
    }
}| 1 | This is a local transformation to be applied during SEMANTIC_ANALYSISphase. | 
This transformation will be applied to those ClassNode instances
annotated with @AsList.
5.6. Compilation errors
If at some point you would like to stop the compilation process the
best approach is to use addError method. This method is available
in both AbstractLocalTransformation and AbstractGlobalTransformation.
package asteroid.local.samples
import asteroid.Phase
import asteroid.AbstractLocalTransformation
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.AnnotationNode
@CompileStatic
@Phase(Phase.LOCAL.SEMANTIC_ANALYSIS)
class GrumpyImpl extends AbstractLocalTransformation<Grumpy, ClassNode> {
    @Override
    void doVisit(AnnotationNode annotation, ClassNode clazz) {
        addError("I don't like you Argggg!!!!! (said the Grumpy transformation)", clazz)
    }
}5.7. Checks
There are many times when you have to check if all precoditions are correct before applying a given transformation. Without this sanity check, many things could go wrong. Checks labels are an effort to avoid boiler plate code when checking the AST state. They are inspired in Spock blocks.
By default checks labels are available in Asteroid local
transformations. All you have to do is to structure your code using
labels check and then.
Here’s an example, it’s a bit silly but I think it will easy to
understand. We have a annotation called @Serializable.
The transformation SerializableImpl will make all classes annotated
with @Serializable to implement java.io.Serializable.
package asteroid.local.samples
import asteroid.Local
@Local(SerializableImpl)
@interface Serializable {}As constraints I want to make sure:
- 
The annotated class package name should should start by 'asteroid' 
- 
The annotated class can only have two method at most 
package asteroid.local.samples
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.AnnotationNode
import asteroid.A
import asteroid.Phase
import asteroid.AbstractLocalTransformation
@CompileStatic
@Phase(Phase.LOCAL.INSTRUCTION_SELECTION)
class SerializableImpl extends AbstractLocalTransformation<Serializable, ClassNode> {
    @Override
    void doVisit(AnnotationNode annotation, ClassNode classNode) {
        check: 'package starts with asteroid'
        classNode.packageName.startsWith('asteroid') (1)
        check: 'there are least than 2 methods'
        classNode.methods.size() < 2 (2)
        then: 'make it implements Serializable and Cloneable'
        A.UTIL.NODE.addInterfaces(classNode, java.io.Serializable, Cloneable) (3)
    }
}| 1 | Checking the annotated class belongs to a certain package | 
| 2 | Checking that the annotated node has less than two methods | 
| 3 | Transformation code | 
To prove it, there’s a test with an annotated class having two methods:
    void testFailsBecauseNumberOfMethods() {
        shouldFail '''
        package asteroid.local.samples
        @Serializable
        class A {
            def a() {}
            def b() {}
        }
        '''
    }And the test… passes :)
5.7.1. Your own transformations
If you would like to add this functionality in your project, you can use Asteroid utility functions to inject this behavior in your code.
A.UTIL.NODE.addCheckTo(A.UTIL.NODE.findMethodByName(annotatedNode, METHOD_DOVISIT));This call is taken from Asteroid local transformations. Checking is added to method doVisit.
6. Global Transformations
Global AST transformation are similar to local one with a major difference: they do not need an annotation, meaning that they are applied globally, that is to say on each class being compiled. It is therefore very important to limit their use to last resort, because it can have a significant impact on the compiler performance.
6.1. Overview
Asteroid suggest a certain way of creating global AST
transformations. Instead of creating a global transformation and
manipulate the SourceUnit directly, an Asteroid global
transformation only holds references to code transformers.
In order to create a global transformation you need to:
- 
Create an implementationof the transformation extendingAbstractGlobalTransformation
- 
Create as many transfomersas you need and then make thegetTransformersmethod from your transformation to return the classes of those transformers.
- 
Your implementation should be annotated by @Phasewith the proper local compilation phase value set.
- 
Add a transformation descriptorin your classpath to tell the compiler where it can find your transformation
6.2. Example
package asteroid.global.samples
import static asteroid.Phase.GLOBAL
import groovy.transform.CompileStatic
import asteroid.Phase
import asteroid.AbstractGlobalTransformation
import asteroid.transformer.Transformer
@CompileStatic
@Phase(GLOBAL.CONVERSION) (1)
class AddTransformation extends AbstractGlobalTransformation { (2)
    @Override
    List<Class<Transformer>> getTransformers() {
        return [AddPropertyToInnerClass, AddTraitTransformer] (3)
    }
}| 1 | Declaring class as a global AST transformation | 
| 2 | Extending asteroid.global.GlobalTransformationImpl | 
| 3 | Adding asteroid.global.AbstractClassNodeTransformerclasses | 
A global transformation needs to be annotated with the
@GlobalTransformation annotation, then it should extend
GlobalTransformationImpl and finally to provide a list of the
transformers that will eventually transform the code.
In this example the code of the transformer is the following:
package asteroid.global.samples
import groovy.transform.CompileStatic
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.ast.ClassNode
import asteroid.A
import asteroid.transformer.AbstractClassNodeTransformer
@CompileStatic
class AddPropertyToInnerClass extends AbstractClassNodeTransformer { (1)
    AddPropertyToInnerClass(final SourceUnit sourceUnit) {
        super(sourceUnit,
              A.CRITERIA.byClassNodeNameContains('AddTransformerSpecExample$Input')) (2)
    }
    @Override
    void transformClass(final ClassNode target) { (3)
        A.UTIL.NODE.addInterfaces(target, java.io.Serializable)
    }
}| 1 | Because this transformer targets class nodes it extends ClassNodeTransformer | 
| 2 | Every ClassNodeTransformerrequires aSourceUnitand a
criteria to filter class nodes | 
| 3 | Then the programmer should only be focused on develop de
transformation within the transformClassmethod | 
Finally add the descriptor file to your classpath at
META-INF/services/ the descriptor file should be named
org.codehaus.groovy.transform.ASTTransformation, and it will
contain the fully qualified name of your AST transformation implementation:
asteroid.global.samples.AddTransformationImpl.
| If you are using GradleorMaventhe transformation
descriptor will be normally found atsrc/main/resources/META-INF/org.codehaus.groovy.transform.ASTTransformation. | 
| Remember that for any new global transformation you should add the new qualified class in a new line. | 
6.3. Transformers
Because a global AST transformation can act over the whole source
code, we use transformers to focus only on certain parts of
it. Transformers theirselves declare which type of nodes they are
interested in, but, they also use criterias to narrow the search.
6.3.1. ClassNodeTransformer
This type of transformers only focuses on transforming a specific set
of ClassNode instances from the AST.
class AddImportTransformer extends AbstractClassNodeTransformer { (1)
    public AddImportTransformer(final SourceUnit sourceUnit) {
        super(sourceUnit,
              A.CRITERIA.byAnnotationSimpleName('AddImport')) (2)
    }
    /**
     * {@inheritDocs}
     */
    @Override
    void transformClass(final ClassNode target) { (3)
        A.UTIL.NODE.addImport(target, groovy.json.JsonOutput) (4)
    }
}| 1 | Extending ClassNodeTransformerwe are only interested inClassNodeinstances | 
| 2 | Then we use a criteriato declare we’re only interested inClassNodeinstances annotated by an annotation which has a simple
nameAddImport | 
| 3 | Overriding the transformClassmethod we will be receiving the
expectedClassNode | 
| 4 | We don’t return anything because we are modifying the node, we are not supposed to replace it in the AST. | 
| Why simple name? Well depending on the compilation phase you
are targeting the information about the class may be not available,
that means it’s fully qualified name. | 
| Transforming an AST node here means to add/remove elements from the AST node. | 
6.3.2. Expression transformers
This type of transformers only focuses on replacing certain expressions found along the AST.
In the following example, we are interested in replacing all method
calls xxx() by a constant number 1.
class ChangeTripleXToPlusOne
    extends AbstractExpressionTransformer<MethodCallExpression> { (1)
    ChangeTripleXToPlusOne(final SourceUnit sourceUnit) {
        super(MethodCallExpression,
              sourceUnit,
              A.CRITERIA.byExprMethodCallByName('xxx')) (2)
    }
    @Override
    Expression transformExpression(final MethodCallExpression target) { (3)
        return A.EXPR.constX(1)  (4)
    }
}| 1 | We declare this transformer is focused on MethodCallExpressionelements | 
| 2 | We declare we are only interested on method calls with name xxx | 
| 3 | Overriding the transformExpression operation we will be receiving the expected node | 
| 4 | Finally will be returning the expression that will replace the former expression in the AST | 
| It’s very important to notice the fact that we are here replacing an expression cause expressions are considered as values. | 
7. Criterias
Every time we want to apply a transformation we want to target specific nodes:
- 
A method with a specific annotation
- 
A class with a specific annotation
- 
A method with a specific name
- 
A method with a specific name anda specific annotation
- 
… 
Instead of holding a node reference and start calling methods from that reference in order to find out which is the node we are interested in, we can use criterias.
| Criterias are available directly as static nodes in the asteroid.Criteriasor viaA.CRITERIA | 
7.1. Transformers
Lets say we would like to apply a given transformation to a specific set of classes annotated with a certain annotation.
class AddImportTransformer extends AbstractClassNodeTransformer { (1)
    public AddImportTransformer(final SourceUnit sourceUnit) {
        super(sourceUnit,
              A.CRITERIA.byAnnotationSimpleName('AddImport')) (2)
    }
    /**
     * {@inheritDocs}
     */
    @Override
    void transformClass(final ClassNode target) { (3)
        A.UTIL.NODE.addImport(target, groovy.json.JsonOutput) (4)
    }
}| 1 | Using a class node transformer | 
| 2 | Looking for a class node annotated with an annotation with name AddImport | 
What about looking for a ClassNode representing an inner class
having a specific name.
package asteroid.global.samples
import groovy.transform.CompileStatic
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.ast.ClassNode
import asteroid.A
import asteroid.transformer.AbstractClassNodeTransformer
@CompileStatic
class AddPropertyToInnerClass extends AbstractClassNodeTransformer { (1)
    AddPropertyToInnerClass(final SourceUnit sourceUnit) {
        super(sourceUnit,
              A.CRITERIA.byClassNodeNameContains('AddTransformerSpecExample$Input')) (2)
    }
    @Override
    void transformClass(final ClassNode target) { (3)
        A.UTIL.NODE.addInterfaces(target, java.io.Serializable)
    }
}| 1 | Again looking for a class node | 
| 2 | With a name containing a specific Outer$Innerclass name | 
Now a different example. Instead of targeting a ClassNode we would
like to find a specific method call expression. We know what is the
name of the method we are calling, that’s why we use
methodCallByName criteria here:
class ChangeTripleXToPlusOne
    extends AbstractExpressionTransformer<MethodCallExpression> { (1)
    ChangeTripleXToPlusOne(final SourceUnit sourceUnit) {
        super(MethodCallExpression,
              sourceUnit,
              A.CRITERIA.byExprMethodCallByName('xxx')) (2)
    }
    @Override
    Expression transformExpression(final MethodCallExpression target) { (3)
        return A.EXPR.constX(1)  (4)
    }
}7.2. and / or
Sometimes using only one criteria could be limiting, sometimes we may
want to combine two or more criterias at once. For that purpose you can
use A.CRITERIA.and and A.CRITERIA.or. Check next example:
class AddLoggerTransformer extends AbstractClassNodeTransformer {
    static final Closure<Boolean> CRITERIA = (1)
        A.CRITERIA.with {
            and(byClassNodeNameStartsWith('asteroid.global.samples'),
                or(byClassNodeNameContains('Logger'),
                   byClassNodeNameEndsWith('Example')))
        }
    AddLoggerTransformer(final SourceUnit sourceUnit) {
        super(sourceUnit, CRITERIA) (2)
    }
    @Override
    void transformClass(final ClassNode target) { (3)
        target.addAnnotation(A.NODES.annotation(Log).build())
    }
}| 1 | Criteria looks for class nodes with name starting with asteroid.global.samplesand nodes containingLoggeror nodes
ending withExample | 
| 2 | Applying the criteria in the constructor | 
| 3 | Adding a note to filtered nodes | 
7.3. As predicates
Although criteras were meant to be used in transformers, the way they
were designed makes them perfectly valid to be used as predicates when
filtering lists of AST nodes. The following example ask for all method
nodes from a given class node and then as any Groovy list tries to
find all methods that comply with the filter passed as parameter: all
methods annotated with the Important annotation and with a name
starting with get.
        List<MethodNode> notSoImportantMethods = classNode
            .methods
            .findAll(A.CRITERIA.and(A.CRITERIA.byAnnotation(Important),
                                    A.CRITERIA.byMethodNodeNameStartsWith('get')))Because criterias are just closures that eventually return a boolean value they fit perfectly in this scenario.
8. Fluent API
| With the upcoming groovy-macromodule in Groovy 2.5.0 most
of the code in Asteroid, used for creatingexpressionsandstatementsmay be gone for good in favor of themacromethod. | 
The main goal of this project is to have a unified way to access all AST APIs through a single entry point. That
entry point is asteroid.A.
- 
NODES: Create instances of org.codehaus.groovy.ast.ASTNode
- 
EXPRESSIONS: Create instances of org.codehaus.groovy.ast.expr.Expression
- 
STATEMENTS: Create instances of org.codehaus.groovy.ast.stmt.Statement
- 
MODIFIERS: asteroid.A.ACC
- 
CHECKERS: asteroid.A.CHECK. Access to checkers.
- 
UTILS: asteroid.A.UTIL.
| The project has been developed having in mind to get the general idea reading this documentation and then checking the specifics using the javadoc. | 
Please check out the javadoc here
8.1. Nodes
Check javadoc: asteroid.A.NODES
This is entry point accesses to a set of builders to create instances of org.codehaus.groovy.ast.ASTNode. All builders
in asteroid.A.NODES follow the same API:
A.NODES.annotation("MyAnnotation") (1)
       .member(...) (2)
       .xxxxxx(...)
       .yyyyyy(...)
       .build()  (3)| 1 | Creates a builder of a specific type of ASTNode. | 
| 2 | Then each node builder has a set of associated methods. E.g: annotationhasmember(…)to add annotation members… | 
| 3 | Once the node has been configured is time to create the instance calling the build()method of the
current builder. | 
For instance, the following code:
A.NODES.annotation("MyAnnotation")
       .member("message", A.EXPR.constX("hello"))
       .member("sort", A.EXPR.constX("desc"))
       .build()Will produce the following annotation:
@MyAnnotation(message = "hello", sort = "desc")| The nodes related javadoc has the same structure. Normally you’ll see the AST and the resulting code explained. | 
8.2. Expressions
Check javadoc: asteroid.A.EXPR
This entry point accesses to a set of methods returning instances of org.codehaus.groovy.ast.expr.Expression.
Unlike the asteroid.A.NODES this entry point only has methods creating expressions directly. So bear in mind that
all methods from asteroid.A.EXPR will return instances of org.codehaus.groovy.ast.Expression.
For instance if we would like to create an expression like:
println "hello"We should code:
A.EXPR.callThisX( (1)
    "println",
    A.EXPR.constX("hello") (2)
)| 1 | Creates an instance of org.codehaus.groovy.ast.expr.MethodCallExpression | 
| 2 | Creates an instance of org.codehaus.groovy.ast.expr.ConstantExpressionused as method call argument. | 
| Why using callThisXwhen codingprintln? This is becauseprintlnis added to all JDK objects
through theDefaultGroovyMethodsobject. | 
8.3. Statements
Check javadoc: asteroid.A.STMT
In computer programming a statement is the smallest standalone element of an imperative programming language that expresses some action to be carried out. It is an instruction written in a high-level language that commands the computer to perform a specified action.[1] A program written in such a language is formed by a sequence of one or more statements. A statement may have internal components (e.g., expressions).
This entry point accesses to a set of methods returning instances of org.codehaus.groovy.ast.stmt.Statement. It
follows the same design as expressions. Each method returns an statement. For instance, a return statement:
return 1Can be written as:
A.STMT.returnS(A.EXPR.constX(1))8.4. Modifiers
Check javadoc: asteroid.A.ACC
When creating a node we may want to restrict its visibility, or mark it as static…etc. To do that each node
has a method called setModifiers(int). That value can be built with one or more values from this entry point:
A.ACC.ACC_PUBLIC // public8.5. Utils
Check javadoc: asteroid.A.UTIL