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.

Apache

The Asteroid project is open sourced under the Apache 2 License.

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

In order to use `Asteroid` in your Groovy project just add the jcenter repository:

``````repositories {
jcenter()
}``````

``compile 'com.github.grooviter:asteroid:0.3.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 greet``````

For a local transformation only two things are required:

• The annotation used as a marker. In this example the `@WithLogging` annotation

• 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 `@Local` annotation assumes the annotation is applied to a `type` (classes). So if you are using the annotation for a type then you could omit `value` and `applyTo` attributes 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
import org.codehaus.groovy.ast.expr.Expression

@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)

}

Statement printlnS(String message) {
return A.STMT.stmt(A.EXPR.callThisX("println", A.EXPR.constX(message))) (5)
}
}``````
 1 The `@Phase` annotation indicates in which compilation phase this transformation will be applied. 2 Building `println "start"` code, which is wrapped in a `org.codehaus.groovy.ast.stmt.Statement` 3 Building `println "end"` code, which is wrapped in a `org.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 constantValue` expression

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-macro` module in Groovy 2.5.0 most of the code in Asteroid, used for creating `expressions` and `statements` may be gone for good in favor of the `macro` method.

#### 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

• 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

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.

BinaryExpression
``1 == 1``
• constant expression 1

• token ==

• constant expression 1

Method call expression
``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 Statement
``````if(booleanExpression) {
println "hello" // statement
}``````
• expression to evaluate

• statement to be executed if the boolean expression evaluates to true

Block Statement
``````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

Block Statement (Cont.)
``````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 Node
``````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.
— Groovy official site

### 5.1. Overview

In order to create a local transformation you need to:

• Create an `annotation` annotated by `@Local`

• Create an `implementation` of the transformation extending `AbstractLocalTransformation`

• Your implementation should be annotated by `@Phase` with 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:

Local annotation transformation

### 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.

annotation
``````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 `applyTo` parameter. That’s why `applyTo` doesn’t receive a list of possible targets. `ANNOTATED` is 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 a `instanceof` to dispatch your actions.
implementation
``````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:

example
``````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 `@Phase` and the correspondent compilation phase. 2 Creating a class extending `AbstractLocalTransformation` and declaring that the annotation and the affected node type are `ToMD5` and `FieldNode` respectively 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 versions `SourceUnit` was 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:

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_ANALYSIS` phase.

This transformation will be applied to those `ClassNode` instances annotated with `@AsList`.

Groovy friendly

When used over a local transformation implementation in Groovy, apart from indicating the compilation phase, underneath, it saves some of the boilerplate code needed to implement an instance of `asteroid.local.AbstractLocalTransformation`.

Although you can create an `AbstractLocalTransformation` in plain Java, you then will have to annotate your transformations like the old days.

### 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'
}
}``````
 1 Checking the annotated class belongs to a certain `package` 2 Checking that the annotated node has less than two methods 3 Transformation code
Limitations

Please notice at the moment checks only have a very limited functionality. They only allow a one-line expression. And these expressions can only see `doVisit` parameter values.

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 :)

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.
— Groovy official site

### 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 `implementation` of the transformation extending `AbstractGlobalTransformation`

• Create as many `transfomers` as you need and then make the `getTransformers` method from your transformation to return the classes of those transformers.

• Your implementation should be annotated by `@Phase` with the proper local compilation phase value set.

• Add a `transformation descriptor` in 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() {
}
}``````
 1 Declaring class as a global AST transformation 2 Extending `asteroid.global.GlobalTransformationImpl` 3 Adding `asteroid.global.AbstractClassNodeTransformer` classes

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.expr.Expression
import org.codehaus.groovy.ast.ClassNode

import asteroid.A
import asteroid.transformer.AbstractClassNodeTransformer

@CompileStatic
class AddPropertyToInnerClass extends AbstractClassNodeTransformer { (1)

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 `ClassNodeTransformer` requires a `SourceUnit` and a criteria to filter class nodes 3 Then the programmer should only be focused on develop de transformation within the `transformClass` method 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 `Gradle` or `Maven` the transformation descriptor will be normally found at `src/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. ClassNodeTransformer ``````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 `ClassNodeTransformer` we are only interested in `ClassNode` instances 2 Then we use a `criteria` to declare we’re only interested in `ClassNode` instances annotated by an annotation which has a simple name `AddImport` 3 Overriding the `transformClass` method we will be receiving the expected `ClassNode` 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`. ExpressionTransformer ``````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 `MethodCallExpression` elements 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 `and` a 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.Criterias` or via `A.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. Criteria ``````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.expr.Expression 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)
}
}``````
 1 Again looking for a class node 2 With a name containing a specific `Outer\$Inner` class 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:

ExpressionTransformer
``````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')))
}

super(sourceUnit, CRITERIA) (2)
}

@Override
void transformClass(final ClassNode target) { (3)
}
}``````
 1 Criteria looks for class nodes with name starting with `asteroid.global.samples` and nodes containing `Logger` or nodes ending with `Example` 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```.

Criterias in Local Transformations
``````        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-macro` module in Groovy 2.5.0 most of the code in Asteroid, used for creating `expressions` and `statements` may be gone for good in favor of the `macro` method.

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.

### 8.1. 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: `annotation` has `member(…​)` 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

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.ConstantExpression` used as method call argument.
 Why using `callThisX` when coding `println` ? This is because `println` is added to all JDK objects through the `DefaultGroovyMethods` object.

### 8.3. Statements

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).
— Wikipedia

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 1``

Can be written as:

``A.STMT.returnS(A.EXPR.constX(1))``

### 8.4. Modifiers

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 // public``