AST Transformations
[ 196 ]
Here, we create a class node for PrettyAwesomeTrait using the ClassHelper class.
We don't want to add this trait if a class already extends it, so we check if the class
already implements PrettyAwesomeTrait before continuing. If it does not, then we
can programmatically add this interface to the class.
Because traits are not just a simple interface implementation, we also need to use
the new TraitComposer class from Groovy 2.4.0. TraitComposer has the method
doExtendTraits, which does all the heavy lifting for us to build the trait AST and
generate all the delegation code that implements the trait. TraitComposer needs
access to the CompilationUnit object, so we need to implement a new interface on
our transformation class CompilationUnitAware. This interface provides a hook
setCompilationUnit, which will be called by the compiler to give access to the
compilation unit object in our transform:
@PrettyAwesome
class Customer {
int id
String firstName
String surname
String street
String city
}
given:
def customer = new Customer(id:1001,firstName:"Fred",
surname:"Flintstone",
street:"1 Rock Road",city:"Bedrock")
when:
customer.prettyPrint()
then:
"""city: Bedrock
firstName: Fred
id: 1001
street: 1 Rock Road
surname: Flintstone""" == output()
Now, we have a @PrettyAwesome annotation that works as expected, except that it
implements the prettyPrint method via trait. This is a really simple application
of a trait in an AST. However, I encourage you to think of traits as a means of
implementing AST transformations. Remember that unlike interfaces, traits are also
able to contain properties, so they can seriously reduce the effort in implementing an
AST transformation.
http://www.ebook3000.com