CHAPTER 3

image

More Advanced Groovy

Chapters 1 and 2 offered a glimpse into the power and capabilities of Groovy by providing a basic explanation of its language features and tools. But there is far more to know about Groovy. For example, Groovy provides an ideal framework for creating unit tests. It makes working with XML simple and straightforward, and it includes a great framework for templating text. Finally, Groovy has a metaprogramming model that you can use to do amazing things, such as enabling the creation of domain-specific languages and adding methods and functionality to the Java API classes, including classes that are marked as final.

This chapter covers a variety of unrelated or loosely related advanced Groovy topics. It starts off by introducing the object-orientation in Groovy, shows you how to use Groovy to write and execute unit tests, compares how to process XML documents with both Java and Groovy, and explains how you can use Groovy’s templating to generate e-mails. The chapter concludes with metaprogramming topics: implementing expando classes, extending classes with metaobject protocol (MOP), AST transformations, and creating domain-specific languages (DSLs).

Object Orientation in Groovy

Few Java purists like to engage in reductio ad absurdum in this manner: “Because you can write scripts in Groovy and scripting is not real programming, Groovy is just a scripting language.” (Note the emphasis on “just.”)This is absolutely not the case. Groovy supports all object-oriented (OO) programming concepts that are well-known to Java developers: classes, objects, interfaces, inheritance, polymorphism, and others. In fact, unlike Java, Groovy is a pure OO language in which everything is an object. You can fully organize your code into classes and packages and still use scripts with classes. The Groovy compiler will convert such scripts into Java classes; that is to say, you can mix scripts with classes.

Classes and Scripts

As Larry Wall says:

“Suppose you went back to Ada Lovelace and asked her the difference between a script and a program. She’d probably look at you funny, and then say something like: ‘Well, a script is what you give the actors, but a program is what you give the audience.’ ”

A lot of entities in Groovy are classes. Lists, sets, maps, strings, patterns, scripts, closures, functions, and expandos are all implemented under the hood as classes. Classes are the building blocks of Groovy. In Groovy, any code that is not inside a class is called a script. You cannot have any Java code outside of a class. In Java, you can have only one public class in a single file, and the name of that class must match the name of the file. In Groovy, in addition to classes, you can have scripts and the same file can include one or more classes in addition to scripting code. Unlike Java, Groovy also allows you to have more than one public class in the same file, and none of the public classes in the same file need to match the name of the containing file.

Listing 3-1 illustrates a file named Hello.groovy with a script but no classes. The compiler will generate a class for the scripting code based on the filename. Compiling this file via groovyc will generate one class: Hello.class.

Listing 3-1.  Hello.groovyFile with Script but no Classes

println "hello"

Listing 3-2 illustrates a file named Hello.groovy with a script and one class. Compiling this file via groovyc will generate one class: Hello.class (for scripting code based on the filename) and HelloWorld.class.

Listing 3-2.  Hello.groovy File with Script and One Class

class HelloWorld{
 def hello(){
  println "hello world"
 }
}
println "hello world"

Listing 3-3 illustrates a file named Hello.groovy with a script and one class with the name same as the file name. This file will not get compiled. By default the compiler generates a class for the scripting code based on the filename, and because we already defined a class with that name, the compiler will throw an exception.

Listing 3-3.  Hello.groovy File with Script and One Classwith the Same Name as the File Name

class Hello{
 def hello(){
  println "hello world"
 }
}
println "hello world"

Listing 3-4 illustrates a file named Hello.groovy with a script and two classes. Compiling this file will generate three classes: Hello.class, HelloWorld1.class, and HelloWorld2.class.

Listing 3-4.  Hello.groovy File with Script and TwoClasses

class HelloWorld1{
 def hello(){
  println "hello world"
 }
}
class HelloWorld2{
 def hello(){
  println "hello world"
 }
}
println "hello world"

Listing 3-5 illustrates a file named Hello.groovy with no script and one class. Compiling this file will generate one class: HelloWorld.class, as there is no script in the file.

Listing 3-5.  Hello.groovy File with no Script and One Class

class HelloWorld{
 def hello(){
  println "hello world"
 }
}

Listing 3-6 illustrates a file named Hello.groovy with no script and two classes. Compiling this file will generate two classes: HelloWorld1.class and HelloWorld2.class.

Listing 3-6.  Hello.groovy File with no Script and TwoClasses

class HelloWorld1{
 def hello(){
  println "hello world"
 }
}
class HelloWorld2{
 def hello(){
  println "hello world"
 }
}

Listing 3-7 illustrates a file named Hello.groovy with no script and one class with the same name as the file. Compiling this file will generate one class: Hello.class. Hazard a guess as to why the compiler does not throw an exception this time. It is because there is no script in the file.

Listing 3-7.  Hello.groovy File with no Script and One Classwith the Same Name as File Name

class Hello{
 def hello(){
  println "hello world"
 }
}

Groovy Constructors

In Groovy, a constructor can be called in several ways. Listing 3-8 illustrates calling the constructor Java style. The preferred way is to use named parameters, as illustrated in Listing 3-9.

Listing 3-8.  Calling the Constructor Java Style

class Test{
  def i
  def j
  Test(i, j){
    this.i = i
    this.j = j
  }
}
def test = new Test(1, 2)
assert test.i == 1
assert test.j == 2

Listing 3-9 illustrates calling the constructor using named parameters. As discussed earlier in Chapter 1, Groovy uses the map object to initialize each property. The map is iterated and the corresponding setter is invoked for each map element.

Listing 3-9.  Using Named Parameters

1. class Test{
2.  def i = 0
3.  def j = 0
4. }
5. def test1 = new Test(i:1, j:2)
6. assert test1.i == 1
7. assert test1.j == 2

As can be seen in Line 5 of Listing 3-9, when we pass a map object to a constructor, the parentheses [], which is the part of the map syntax, can be skipped.

We can also list the property values in any order, like so:

def test2= new Test( j:2, i:1)
assert test2.i == 1
assert test2.j == 2

If a property is excluded, the corresponding setter will not be called and the default value is preserved, like so:

def test3 = new Test(i:1)
assert test3.i == 1
assert test3.j == 0

A class can have more than one constructor, as illustrated in Listing 3-10.

Listing 3-10.  Class with More than One Constructor

1.  class A{
2.   def list= []
3.   A(){
4.    list<< "A constructed"
5.   }
6.   A(int i){
7.    this()
8.    list<< "A constructed with $i"
9.   }
10.  A(String s){
11.   this(5)
12.   list<< "A constructed with '$s'"
13.  }
14. }
15. def a1= new A()
16. assert a1.list == [ "A constructed" ]
17. def a2= new A(7)
18. assert a2.list.collect{it} == [
19. "A constructed",
20. "A constructed with 7",
21. ]
22. def a3= new A('test')
23. assert a3.list.collect{it} == [
24. "A constructed",
25. "A constructed with 5",
26. "A constructed with 'test'",
27. ]

In Listing 3-10, class A has three constructors:

  • In Line 7, constructor A(int i) calls default parameterless constructor A() in its first statement.
  • In Line 11, A(String s) calls A(int i) in its first statement, which in turn calls constructor A() in its first statement.
  • In Line 18, when the constructor A(7) is invoked, the list will have two elements, one from A(int i) and one from A(). Note the collect method of list in Line 19. The collect method accepts a list and a closure argument.

Inheritance

As shown in Listing 3-11, Groovy enables one class to extend another, just as interfaces can, though classes extend at most one class.

Listing 3-11.  Extending class

1. class A{}
2. class B extends A{}
3. def b = new B()
4. assert b in B && b in A

In Line 4, we test for extended classes with the in operator.

The in operator is a shortcut for the contains() method in a collection. The snippet below illustrates the usage of the in operator.

assert 1 in [1, 2, 3]

Public instance fields, properties, and methods defined on an extended class are also available on the extending class, as shown in Listing 3-12:

Listing 3-12.  Inherited Methods and Properties

1.  class A{
2.   int x
3.   int y
4.   String methodA(int n){ "value ${x= y= n}" }
5.  }
6.  class B extends A{
7.   String methodB(int n){ "value $n" }
8.  }
9.  def b= new B()
10. assert b.methodB(10) == 'value 10'
11. assert b.methodA(20) == 'value 20'
12. assert b.x == 20
13. assert b.y == 20
14. b.y= 5
15. assert b.y == 5
16. assert b.getY() == 5
  • In Line 11, we call method A() on extending class B.
  • In Lines 12–16 we assert x and y on extending class B.

We can define an abstract class, a class with only some methods defined, the others being only declarations just like in interfaces. As shown in Listing 3-13, an abstract class and each method declaration in it must be modified with the abstract keyword:

Listing 3-13.  Using Abstract Class

1.  interface X{
2.   def x()
3.  }
4.  interface Y{
5.   def y()
6.  }
7.  abstract class A{
8.   def a(){ println 1 }
9.   abstract b()
10. }
11. class B extends A implements X, Y{
12.  def x(){ println 2 }
13.  def y(){ println 3 }
14.  def b(){ println 4 }
15. }
  • In Line 8, a method a() is defined.
  • In Line 9, a method b() is declared.
  • Line 14 provides the definition to method b().

Interface and abstract methods cannot be declared static. Whether a method is static is part of its definition, as shown in Listing 3-14, but not its declaration.

Listing 3-14.  Interface and Abstract Methods

1.  interface X{
2.   def x()
3.   //static x1()
4.  }
5.  interface Y{
6.   def y()
7.  }
8.  abstract class A{
9.   static a(){ println 1 }
10.  abstract b()
11.  abstract c()
12.  //abstract static c1()
13. }
14. class B extends A implements X, Y{
15.  static x(){ println 2 }
16.  def y(){ println 3 }
17.  static b(){ println 4 }
18.  def c(){ println 5 }
19. }
  • Line 3 is commented, as interface methods cannot be declared static.
  • Line 12 is commented, as abstract methods cannot be static.

A final class cannot be extended; a final method cannot be overridden:

Listing 3-15.  Using Final Class

1. class A{
2.  final a(){ 11 }
3.  def b(){ 12 }
4. }
5. final class B extends A{
6.  //def a(){ 15 }
7.  def b(){ 16 }
8. }
9. //class C extends B{}
  • Line 6 is commented, as method a() in class A is final and cannot be overridden.
  • Line 9 is commented, as class B is final and cannot be extended.

Just as a class’s constructor can call another constructor as we discussed in Listing 3-10, so also it can call a constructor on the superclass, as shown in Listing 3-16:

Listing 3-16.  Calling Constructor of the Superclass

1.  class A{
2.   def list= []
3.   A(){
4.    list<< "A constructed"
5.   }
6.   A(int i){
7.    this()
8.    list<< "A constructed with $i"
9.   }
10. }
11. class B extends A{
12.  B(){
13.   list<< "B constructed"
14.  }
15.  B(String s){
16.   super(5)
17.   list<< "B constructed with '$s'"
18.  }
19. }
20. def b1= new B('test')
21. assert b1.list.collect{it} == [
22.  "A constructed",
23.  "A constructed with 5",
24.  "B constructed with 'test'",
25. ]
26. def b2= new B()
27. assert b2.list == [
28.  "A constructed",
29.  "B constructed",
30. ]
  • In Line 16, a constructor can call its 'superclass's constructor if it's the first statement.
  • In Line 27, default parameterless constructor is called, as super() is not called.

Polymorphism

Groovy does not really need interfaces because of dynamic typing but still includes support for them. In Groovy, polymorphism is simply a matter of matching method names. Two objects belonging to two unrelated classes can be sent in the same message, provided that the method is defined by each class, as illustrated in Listing 3-17.

Listing 3-17.  A Polymorphism Example

1.  class Square{
2.   def display() {
3.    assert  "length:${length} width:${width}" == "length:10 width:10"
4.   }
5.   def length
6.   def width
7.  }
8.  class Rectangle{
9.   def display() {
10.   assert  "length:${length} width:${width}" == "length:10 width:12"
11.  }
12.  def length
13.  def width
14. }
15. def shapes= [new Square(length : 10, width : 10),
16.  new Rectangle(length : 10, width : 12)
17. ]
18. shapes.each { item -> item.display() }
  • The two classes Square and Rectangle in Line 1 and Line 8 do not share a common superclass or implement the same interface (in our example).
  • Line 18 iterates over the shapes collection and on each object in the collection, the display() method specific to that object is called.

Groovy Unit Testing

One of Groovy’s best value propositions is unit testing. Using Groovy to unit-test Groovy or Java code can make the code easier to read and maintain. Unit testing is a common way to introduce Groovy to an organization, because it doesn’t affect the production runtime. Once developers and managers get comfortable with Groovy in a testing capacity, they eventually begin using it in production.

Unit testing is so fundamental to Groovy that it’s built right in. You don’t need to download a separate framework. Groovy already includes and extends JUnit, which is a popular Java unit­testing framework. The primary extension is groovy.util.GroovyTestCase, which inherits from junit.framework.TestCase and adds the additional assert methods found in Table 3-1.

Table 3-1. GroovyTestCase Assert Methods

Assert Method Description
assertArrayEquals Asserts two arrays are equal and contain the same values
assertContains Asserts an array of characters contains the given characters or an array of ints contains a given int
assertEquals Asserts two Objects or two Strings are equal
assertInspect Asserts the value of the inspect() method
assertLength Asserts the length of char, int, or Object arrays
assertScript Asserts script runs without any exceptions being thrown
assertToString Asserts the value of toString()

JUnit (and therefore Groovy unit testing) works by creating a class that inherits from TestCase or one of its descendants. GroovyTestCase is the appropriate class to extend for unit testing in Groovy. Notice that GroovyTestCase is found in the groovy.util package, so it is implicitly available and doesn’t even require any imports. Tests can then be added by creating methods that have a name that begins with test and is followed by something descriptive about the test. For example, you could use testAlphaRanges for a test that validates the Groovy language feature of ranges. These test methods should take no parameters and return void. Unlike with JUnit tests written in Java, these methods don’t have to declare exceptions that could be thrown, because Groovy naturally converts all checked exceptions into unchecked exceptions. This makes tests more readable than the equivalent Java implications.

Unit tests often require objects to be put into a known state. In addition, tests should be good test-harness citizens and clean up after themselves. Like JUnit tests, all Groovy tests can override the setUp() and tearDown() methods.

Unit tests are also a great way to learn new frameworks, libraries, and languages such as Groovy. You can use unit tests to validate your understanding of how they work. Listing 3-18 is a unit test used to validate some assumptions about Groovy ranges, including whether a range from 'a'..'z' contains uppercase letters and whether ranges can be concatenated.

Listing 3-18 shows a unit test that extends from GroovyTestCase and contains two variables that include a range of lowercase letters on Line 3 and a range of uppercase letters on Line 4. The test case also contains three tests.

Listing 3-18.  Example Unit Test That Validates Assumptions About Groovy Ranges

1.  class RangeTest extends GroovyTestCase {
2.
3.    def lowerCaseRange = 'a'..'z'
4.    def upperCaseRange = 'A'..'Z'
5.
6.    void testLowerCaseRange() {
7.      assert 26 == lowerCaseRange.size()
8.      assertTrue(lowerCaseRange.contains('b'))
9.      assertFalse(lowerCaseRange.contains('B'))
10.  }
11.
12.   void testUpperCaseRange() {
13.     assert 26 == upperCaseRange.size()
14.     assertTrue(upperCaseRange.contains('B'))
15.     assertFalse(upperCaseRange.contains('b'))
16.   }
17.
18.   void testAlphaRange() {
19.     def alphaRange = lowerCaseRange + upperCaseRange
20.     assert 52 == alphaRange.size()
21.     assert alphaRange.contains('b')
22.     assert alphaRange.contains('B')
23.   }
24. }

The first test in Listing 3-18, shown on Lines 6–10, asserts that the range has a size of 26, representing each of the letters in lowercase. It also asserts that a lowercase 'b' is in the range but that an uppercase 'B' is not. The second test, shown on Lines 12–16, is basically the same test but uses the uppercase range. The third test, on the other hand, validates that the two ranges can be concatenated to produce a new range that includes both. Therefore, the new range is twice the size and includes both the lowercase 'b' and the uppercase 'B'.

Running a Groovy unit test is just like running a script. To run this test, execute the following:

> groovy RangeTest

Because a JUnit test runner is built into Groovy, the results of the tests are printed to standard out. The results identify how many tests ran, how many failed, and how many errors occurred. Failures indicate how many tests did not pass the assertions, and errors indicate unexpected occurrences such as exceptions. In addition, because GroovyTestCase extends JUnit, you can easily integrate the Groovy tests into automated test harnesses such as Apache Ant and Apache Maven builds so they can be run continually.

Working with XML

Extensible Markup Language (XML) is a general-purpose markup language commonly used in enterprise applications to persist or share data. Historically, creating and consuming XML documents has been easier than working with other types of formats, because XML is text-based, follows a standard, is in an easily parseable format, and features many existing frameworks and libraries to support reading and writing documents for many different programming languages and platforms.

Most of these frameworks, however, are based on the World Wide Web Consortium’s (W3C) Document Object Model (DOM), which can cause the code that manipulates XML documents to become difficult to write and read. Due to the popularity and complexity of working with XML, Groovy includes a framework that uses XML in a natural way. The next section demonstrates how complicated it is to write simple XML with standard Java code, then shows you how to process XML in the simple and elegant Groovy way.

Writing XML with Java

Generating a simple XML document like the one found in Listing 3-19 in Java is difficult, time consuming, and a challenge to read and maintain.

Listing 3-19.  Simple XML Output for Todos

<todos>
 <todo id="1">
  <name>Buy Beginning Groovy Grails and Griffon</name>
  <note>Purchase book from Amazon.com for all co-workers.</note>
 </todo>
</todos>

Listing 3-20 shows the minimum Java code necessary to generate the XML shown in Listing 3-19.

Listing 3-20.  Java Code to Generate the Simple Todo XML Found in Listing 3-19

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Result;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;

/**
 * Example of generating simple XML in Java.
 */
public class GenerateXML {
  public static void main (String[] args)
      throws ParserConfigurationException, TransformerException {
    DocumentBuilder builder =
      DocumentBuilderFactory.newInstance().newDocumentBuilder();
    Document doc = builder.newDocument();
    Element todos = doc.createElement("todos");
    doc.appendChild(todos);

    Element task = doc.createElement("todo");
    task.setAttribute("id", "1");
    todos.appendChild(task);

    Element name = doc.createElement("name");
    name.setTextContent("Buy Beginning Groovy Grails and Griffon");
    task.appendChild(name);
    Element note = doc.createElement("note");
    note.setTextContent("Purchase book from Amazon.com for all co­workers.");
    task.appendChild(note);
    // generate pretty printed XML document
    TransformerFactory tranFactory = TransformerFactory.newInstance();
    Transformer transformer = tranFactory.newTransformer();
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
    transformer.setOutputProperty(
        "{ http://xml.apache.org/xslt }indent-amount", "2");
    Source src = new DOMSource(doc);
Result dest = new StreamResult(System.out);
    transformer.transform(src, dest);
  }
}

Notice how difficult it is to read Listing 3-20. It begins by using DocumentBuilderFactory to create a new DocumentBuilder. With DocumentBuilder, the newDocument() factory method is called to create a new Document. Elements are created using DocumentBuilder's factory methods, configured by adding attributes or text content, and then finally appended to their parent element. Notice how difficult it is to follow the natural tree structure of the XML document by looking at the Java code. This is partly because most elements require three lines of code to create, configure, and append the element to its parent.

Finally, outputting the XML into a human­readable nested format isn’t straightforward. Much like creating the document itself, it begins by getting a TransformerFactory instance and then using the newTransformer() factory method to create a Transformer. Then the transformer output properties are configured to indent, and the indent amount is configured. Notice that the indent amount isn’t even standard. It uses an Apache Xalan–specific configuration, which may not be completely portable. Ultimately, a source and result are passed to the transformer to transform the source DOM into XML output.

Groovy Builders

Groovy simplifies generating XML by using the concept of builders, based on the builder design pattern from the Gang of Four. Groovy builders implement a concept of Groovy-Markup, which is a combination of Groovy language features such as MOP (discussed later in the chapter), closures, and the simplified map syntax to create nested tree-like structures. Groovy includes five major builder implementations, as defined in Table 3-2. They all use the same format and idioms, so knowing one builder pretty much means you’ll be able to use them all.

Table 3-2. Groovy Builders

Name Description
AntBuilder Enables the script and execution of Apache Ant tasks
DOMBuilder Generates W3C DOMs
MarkupBuilder Generates XML and HTML
NodeBuilder Creates nested trees of objects for handling arbitrary data
SwingBuilder Creates Java Swing Uls (discussed in detail in Chapter 13 )

In general, you start using a builder by creating an instance of the builder. Then you call a named closure to create the root node, which could represent a root XML element, a Swing component, or a specific builder-appropriate node. You add nodes by nesting more named closures. This format makes it easy to read the hierarchical structures.

You add attributes by using Groovy’s map syntax to pass name-value pairs into the named closures. Under the covers, MOP interprets the message passed to the object, usually to determine which method to invoke. When it realizes there is no method by that name, it creates the associated node or attribute.

Writing XML with Groovy MarkupBuilder

As noted in Table 3-2, you use MarkupBuilder to create XML and HTML. Listing 3-21 shows the Groovy code in action for creating the XML shown in Listing 3-19.

Listing 3-21.  Groovy Code to Generate the Simple Todo XML Found in Listing 3-19

1.  def writer = new StringWriter()
2.  def builder = new groovy.xml.MarkupBuilder(writer)
3.  builder.setDoubleQuotes(true)
4.  builder.todos {
5.      todo (id:"1") {
6.          name "Buy Beginning Groovy Grails and Griffon"
7.          note "Purchase book from Amazon.com for all co-workers."
8.      }
9.  }
10.
11. println writer.toString()

Looking at Listing 3-21, you can see how much easier it is to read than the Java equivalent shown in Listing 3-20. The example begins by creating StringWriter, so the final XML can be printed to system out on Line 11. Then MarkupBuilder is created using StringWriter. By default, MarkupBuilder uses single quotes for attribute values, so Line 3 changes the quotes to double quotes to comply with the XML specification. Lines 4–9 actually build the XML document using named closures and map syntax. You can easily see that todos contains a todo with an id attribute of 1 and nested name and note elements.

Reading XML with XmlSlurper

Groovy makes reading XML documents equally as easy as writing XML documents. Groovy includes the XmlSlurper class, which you can use to parse an XML document or string and provide access to a GPathResult. With the GPathResult reference, you can use XPath-like syntax to access different elements in the document.

Listing 3-22 shows how to use XmlSlurper and GPath to interrogate a todos XML document.

Listing 3-22.  Reading XML in Groovy

1. def todos = new XmlSlurper().parse('c:/todos.xml')
2. assert 3 == todos.todo.size()
3. assert "Buy Beginning Groovy Grails and Griffon" == todos.todo[0].name.text()
4. assert "1" == todos.todo[0][email protected]()

Listing 3-22 begins by using XmlSlurper to parse a todos.xml file containing three todo items. Line 2 asserts there are three todos in the document. Line 3 shows how to access the value of an element, while Line 4 shows how to access the value of an attribute using @.

Native JSON support

As JSON becomes a natural choice for data- interchange format, Groovy added native support for JSON.

Reading JSON

A JsonSlurper class allows you to parse JSON payloads, and access the nested Map and List data structures representing that content. We illustrated JsonSlurper for reading JSON in Chapter 1.

JsonBuilder

Groovy introduces JsonBuilder to produce JSON content, as illustrated in Listing 3-23.

Listing 3-23.  Reading XML in Groovy

1.  import groovy.json.*
2.  def builder = new groovy.json.JsonBuilder()
3.  def root = builder.Book{
4.   Groovy{
5.    title 'Beginning Groovy, Grails and Griffon'
6.    Authors(
                    1: 'Vishal',
                    2: 'Chris',
                    3: 'Joseph',
                                   4: 'James',
7.    )
8.   }
9.  }
10. println builder.toString()

The output is:

{"book":{"groovy":{"title":"Beginning Groovy, Grails and Griffon", "authors":{"1"
:"Vishal","2":"Chris","3":"Joseph","4":"James"}}}}

Prettyprinting JSON Content

As you can see, the output of Listing 3-23 is difficult to read. When given a JSON data structure, you may wish to prettyprint it, so that you can more easily inspect it, with a friendlier layout. So for instance, if you want to prettyprint the result of the previous example, there are two ways to fix it. We can use either the JsonBuilder or JsonOutput.

In the case of JsonBuilder, we can just replace Line 16 in Listing 3-23 with:

println builder.toPrettyString()

In the case of JsonOutput, replace Line 16 in Listing 3-23 with this line:

println JsonOutput.prettyPrint(builder.toString())

Either of these ways gives the same output:

{
            "book": {
                      "groovy": {
                              "title": "Beginning Groovy, Grails and Griffon",
                              "authors": {
                                        "1": "Vishal",
                                        "2": "Chris",
                                        "3": "Joseph",
                                        "4": "James"
                               }
                     }
           }
}

Generating Text with Templates

Many web applications generate text for e-mails, reports, XML, and even HTML. Embedding this text in code can make it difficult for a designer or business person to maintain or manage. A better method is to store the static portion externally as a template file and process the template when the dynamic portions of the template are known.

As shown in Table 3-3, Groovy includes three template engines to make generating text easier.

Table 3-3 .  Groovy Template Engines

Name Description
SimpleTemplateEngine Basic templating that uses Groovy expressions as well as JavaServer Pages (JSP) <% %> script and <%= %> expression syntax
GStringTemplateEngine Basically the same as SimpleTemplateEngine, except the template is stored internally as a writable closure
XmlTemplateEngine Optimized for generating XML by using an internal DOM

The SimpleTemplateEngine usually is appropriate for most templating situations. Listing 3-24 shows an HTML e-mail template that we’ll use in Chapter 11 for sending e-mail during a nightly batch process.

Listing 3-24.  HTML E-mail Template Found in nightlyReportsEmail.gtpl

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>
    Collab-Todo Nightly Report for
    ${String.format('%tA %<tB %<te %<tY', date)}
</title>
</head>

<body bgcolor="#FFFFFF" style="margin:0;padding:0;">
<div style="padding: 22px 20px 40px 20px;background-color:#FFFFFF;">
<table width="568" border="0" cellspacing="0" cellpadding="1"
        bgcolor="#FFFFFF" align="center">
<tr>
<td>
          Dear ${user?.firstName} ${user?.lastName},
<p />
          Please find your attached nightly report for
          ${String.format('%tA %<tB %<te %<tY', date)}.
</td>
</tr>
</table>
<!-- static HTML left out for brevity -->
</div>
</body>
</html>

The template in Listing 3-24 is mostly HTML with a couple of Groovy expressions thrown in for the dynamic portions of the e-mail, such as formatting the date and user’s name. The e-mail produces the image shown in Figure 3-1.

9781430248064_Fig03-01.jpg

Figure 3-1 .  HTML e-mail

To process the template, you create an instance of a template engine and use the overloaded createTemplate() method, passing it a File, Reader, URL, or String containing the template text to create a template. Now with the template loaded and parsed, you call the make() method, passing it a map that binds with the variables in the template that are based on the names in the map. Listing 3-25 shows what the code that generates the e-mail in Figure 3-1 looks like.

Listing 3-25.  E-mail Template-Processing Code

1.  import groovy.text.SimpleTemplateEngine
2.
3.  /**
4.  * Simple User Groovy Bean.
5.  */
6.  class User {
7.   String firstName;
8.   String lastName;
9.  }
10.
11. def emailTemplate = this.class.getResource("nightlyReportsEmail.gtpl")
12. def binding = [
13.    "user": new User(firstName: "Christopher", lastName: "Judd"),
14.    "date": new Date()
15. ]
16. def engine = new SimpleTemplateEngine()
17. def email = engine.createTemplate(emailTemplate).make(binding)
18. def body = email.toString()
19.
20. println body

Listing 3-25 begins by importing the SimpleTemplateEngine on Line 1 so you have access to it on Line 16. Lines 6–9 declare a simple User GroovyBean. The template is loaded from the nightlyReportsEmail.gtpl file found on the classpath on Line 11. It contains the template text found in Listing 3-24. Lines 12–15 create the map containing the passed user and date data, which will be bound to the template when the template is processed. SimpleTemplateEngine, created on Line 16, is used on Line 17 to create and process the template.

Runtime Metaprogramming

With metaprogramming you can inspect, intercept, and alter the behavior of a program at runtime. In general, statically typed languages discard their type information during compilation, and thus are incapable of reflection. Some static languages (such as Java) exhibit ability to change the runtime behavior of a program; however, this ability is only limited or restrictive in order to prevent subverting or destabilizing the type system. The key features that contribute to runtime dynamicity are introspection (the ability of a program to examine itself) and intercession (the ability of a program to alter its structure).Dynamic languages such as Groovy provide these capabilities through the metaobject protocol (MOP).

Metaobject Protocol

The metaobject protocol was originally defined as an interface to a programming language to give the user of the language the ability to modify the language’s behavior and even its implementation. Before long, programming languages were more or less abstractions with constructs you could build on top of them. The metaobject protocol approach draws a distinction by opening up languages to the user to regulate, so you can fine-tune the language’s behavior and implementation. MOP, formally, is the collection of rules of how a request for a method call will be handled by the Groovy runtime system. Every method call that you write in Groovy is redirected to MOP. Even if the call-target (the target of the method call) was compiled with Groovy or Java, the method call is still redirected to MOP; for example, the Groovy compiler generates byte code for the method call that calls into the MOP. The key responsibility of MOP is finding and selecting the target and handling the case when the target cannot be found. In order to discover the call-target for the method call, MOP needs information, which is stored in metaclasses.

Metaclass

A metaclass provides the means of reflection and dynamic invocation. Groovy never calls methods directly in the byte code but always through the object’s metaclass. In Groovy, all classes, including all Java classes, have a property of metaClass of type groovy.lang.MetaClass. When dispatching a message to an object, the metaclass not only helps to determine which behavior should be invoked, but also prescribes behavior if the class does not implement the requested behavior. As shown in Listing 3-26, the metaclass of any class can be found by accessing its .metaClass property.

Listing 3-26.  Accessing Metaclass

1. class Book {
2. String title
3. }
4. def groovyMetaclass = Book.metaClass

Line 4 illustrates how to access the metaclass of class Book.

All Groovy objects implement the groovy.lang.GroovyObject interface, which exposes a getMetaClass() method for each object. Each Groovy object has an associated MetaClass that can be returned via a call to getMetaClass() in the GroovyObject interface, as shown in Listing 3-27.

Listing 3-27.  GroovyObject Interface

public interface GroovyObject {
 Object invokeMethod(String name, Object args);
 Object getProperty(String propertyName);
 void setProperty(String propertyName, Object newValue);
 MetaClass getMetaClass();
 void setMetaClass(MetaClass metaClass);
}

invokeMethod(), getProperty(), and setProperty() make Groovy objects dynamic. You can use them to work with methods and properties created on the fly.

Using invokeMethod() and get/setProperty()

Groovy supports the ability to intercept method and property access via invokeMethod() and get/setProperty().

Overriding invokeMethod

In any Groovy class you can override invokeMethod(), which will essentially intercept method calls. When you a call a method on a Groovy object, the method invocation is dispatched to the invokeMethod() of the object, as illustrated in Listing 3-28.

Listing 3-28.  Overriding invokeMethod

1.  class Item{
2.   int itemNumber
3.   String itemName
4.   int qty
5.   Object invokeMethod(String name, Object args) {
6.    if (name == "test"){
7.     this.properties.each {
8.      println " " + it.key + ": " + it.value
9.     }
10.   }
11.  }
12. }
13. def item1 = new Item(itemNumber: 1, itemName:"Item 1", qty:100)
14. def item2= new Item(itemNumber:2, itemName:"Item 2", qty:200)
15. def itemList = [ item1, item2]
16. itemList.each { it.test()}
  • In Line 16, the test() method is invoked, which does not exist.
  • In Line 5, the invokeMethod() added to the Item class allows for intercept method invocations and responds to calls to test(), even though this method does not exist.

Here is the output from Listing 3-28:

class: class Item
itemName: Item 1
qty: 100
itemNumber: 1
class: class Item
itemName: Item 2
qty: 200
itemNumber: 2

Overriding getProperty and setProperty

You can also override property access using the getProperty and setProperty of the GroovyObject interface illustrated in Listing 3-29.

Listing 3-29.  Overriding getProperty/setProperty

1. class Expandable {
2.  def storage = [:]
3.  def getProperty(String name) { storage[name] }
4.  void setProperty(String name, value) { storage[name] = value }
5. }
6. def e = new Expandable()
7. e.foo = "bar"
8. println e.foo

Using methodMissing()

methodMissing() is only invoked in the case of a failed method dispatch. Listing 3-30 illustrates the usage of methodMissing().

Listing 3-30.  Using methodMissing

1. class Missing{
2.  def methodMissing(String name, args) {
3.   "$name method does not exist"
4.  }
5. }
6. Missing m = new Missing()
7. assert m.test() == "test method does not exist"

When you call a method on a POGO(plain old Groovy Object):

  1. Groovy looks for the method in the metaclass of the POGO.
  2. If the method is not found in the metaclass, then Groovy looks for the method in the POGO itself.
  3. If the method is not found in POGO as well, then Groovy looks to see if the POGO has methodMissing(); if it is there, it is called.
  4. If methodMissing() is not there, Groovy looks to see if the POGO has implemented invokeMethod(). If the POGO has implemented invokeMethod(), it is called.
  5. If the POGO has not implemented invokeMethod(), then the default implementation of invokeMethod() is called, which throws a MissingMethodException.

Expandos

The Expando class is found in the groovy.util package and is a dynamically expandable bean, meaning you can add properties or closures to it at runtime. In some situations we need an object to hold interim behaviors. In such situations, instead of creating a class merely for such behaviors, expandos come in handy so we can create objects dynamically at runtime, as illustrated in Listing 3-31.

Listing 3-31.  Using Expando

1. def multiplier = new Expando()
2. multiplier.value = 5
3. multiplier.doubleIt = { −>  multiplier.value = multiplier.value* 2}
4. multiplier.doubleIt()
5. assert multiplier.value == 10
  • Line1: We create an expando.
  • Line 2: A state ‘5’ is assigned to not-yet-existing property value.
  • Line 3: New behavior is assigned to the not-yet-existing property doubleIt as a closure. After the assignment, it can be called as if it was a method.
  • Line 7: Asserts that a new state ‘10’ is assigned to not-yet-existing property value.

Expandos, when combined with Groovy’s duck typing, are also a great way to implement mock objects during unit testing.

image Note  Duck typing refers to the concept that if it walks like a duck and talks like a duck, it must be a duck. In Groovy speak, if an object has properties and methods similar to another object, the two objects must be of the same type.

The reason for explaining the Expando class here is that there is an expando metaclass in Groovy that is a metaclass that works like an expando. You can add new state (properties) and new behavior (methods) in the metaclass by simply assigning a closure to a property of the metaclass.

ExpandoMetaClass

Groovy has a special metaclass called an ExpandoMetaClass that allows you to add methods, constructors, and properties at runtime by assigning a closure to a property of the metaclass. Every java.lang.Class is supplied with a special metaClass property that, when used, gives you a reference to an ExpandoMetaClass instance.

Borrowing Methods from Other Classes

With ExpandoMetaClass you can also use Groovy’s method pointer syntax to borrow methods, as shown in Listing 3-32, from other classes.

Listing 3-32.  Borrowing Methods from Other Classes

1.  class Author{
2.   String name
3.  }
4.  class Greet{
5.   def sayHello() {
6.    "hello vishal"
7.   }
8.  }
9.  def hello = new Greet()
10. Author.metaClass.greet = hello.&sayHello
11. def author = new Author()
12. assert author.greet() == "hello vishal"
  • In Line 1, class Author does not have any method and class Greet has a method sayHello().
  • Line 10 uses the method closure operator that we introduced in Chapter 2 to assign the sayHello() method to Author using ExpandoMetaclass.
  • On Line 12 we call the method Greet() on Author class. This Greet() method does not exist in Author class and has been borrowed from the Greet class, dynamically creating method names.

Since Groovy allows you to use strings as property names, this in turns allows you to dynamically create method and property names at runtime.

To create a method with a dynamic name, simply use Groovy’s feature of reference property names as strings, as shown in Listing 3-33.

Listing 3-33.  Dynamically Creating Method Names

1.  class Test{
2.   String name = "foo"
3.  }
4.  def newName = "bar"
5.  Test.metaClass."changeNameTo${newName}" = {−> delegate.name = "bar" }
6.  def m = new Test()
7.  assert  m.name == "foo"
8.  m.changeNameTobar()
9.  assert m.name == "bar"
  • In Line 5, we use string interpolation to generate the name of the method, which, when called, will change the name in the test class from “foo” to “bar”.
  • Note that within the scope of the closure, the delegate variable is equivalent to this in a standard method.
  • In Line 8, we use the method changeNameTobar(), which will be generated at run-time.
  • In Line 9, we assert the new value of name, implying the dynamically generated method changed the name as expected.

Adding Constructors

Adding constructors is a little different than adding a method with ExpandoMetaClass. Essentially you use a special constructor property and either use the << or = operator to assign a closure, as shown in Line 4 in Listing 3-34.

Listing 3-34.  Adding Constructors

1.  class Book {
2.  String title
3.  }
4.  Book.metaClass.constructor << { String title -> new Book(title:title) }
5.  def b = new Book("Beginning Groovy, Grails and Griffon")
6.  assert "Beginning Groovy, Grails and Griffon" == b.title

Adding Properties

In Listing 3-35, class Book has only one property: title. We will add the author property to the Book class dynamically.

Listing 3-35.  Addiing Properties

1.  class Book {
2.   String title
3.  }
4.  Book.metaClass.constructor << { String title -> new Book(title:title) }
5.  Book.metaClass.getAuthor << {−> "Vishal Layka" }
6.  def b = new Book("Beginning Groovy, Grails and Griffon")
7.  assert "Beginning Groovy, Grails and Griffon"== b.title
8.  assert "Vishal Layka" == b.author
  • Line 4adds a constructor that will initialize the property title in Book class
  • Line 5adds the property author and assigns it the value “Vishal Layka”.
  • Line 7asserts the existing property title as “Beginning Groovy, Grails and Griffon”.
  • Line 8asserts the non-existing property author as “Vishal Layka”.

Adding Methods on Interfaces

It is possible to add methods onto interfaces with ExpandoMetaClass. However, you must enable this ability globally using the ExpandoMetaClass.enableGlobally() method before application startup.

Listing 3-36 illustrates adding a new method to all implementers of java.util.List:

Listing 3-36.  Adding Methods on Interfaces

1.  List.metaClass.sizeDoubled = {−> delegate.size() * 2 }
2.  def list = []
3.  list << 1
4.  list << 2
5.  assert 4 == list.sizeDoubled()
  • In Line 1, we add a new method sizeDoubled(), which we replace using = operator to the size()method of the list multiplied by 2.
  • In Lines 3-4, we append two elements to the list so the size of the list is 2.
  • In Line 5, we assert sizeDoubled() returns 4.

Adding or Overriding Instance Methods

With ExpandoMetaClass adding or overriding instance methods is effortless, as illustrated in Listing 3-37.

Listing 3-37.  Adding Instance Methods

1.  class Book {
2.   String title
3.  }
4.  Book.metaClass.titleInUpperCase << {−> title.toUpperCase() }
5.  def b = new Book(title:"Beginning Groovy, Grails and Griffon")
6.  assert "BEGINNING GROOVY, GRAILS AND GRIFFON" == b.titleInUpperCase()
  • In Line 4, the left shift << operator is used to “append” the new method. If the method already exists, an exception is thrown. If you want to replace an instance method, you can use the = operator:
    Book.metaClass.toString = {−> title.toUpperCase() }
  • In Line 6, we assert by invoking the newly added titleInUpperCase(), which operates on the String title, the toUpperCase() assigned to it in Line 4.

Adding or Overriding Static Methods

As shown in Listing 3-38, you can also add static methods using the same technique as instance methods with the addition of the static qualifier before the method name:

Listing 3-38.  Adding a Static Method

1.  class Book {
2.   String title
3.  }
4.  Book.metaClass.static.create << { String title -> new Book(title:title) }
5.  def b = Book.create("Beginning Groovy, Grails and Griffon")
6.  assert "Beginning Groovy, Grails and Griffon" == b.title
  • In Line 5, we add a static qualifier before the create method. This method creates a new book object with the title “Beginning Groovy, Grails and Griffon”.

Categories

Categories are used to add methods to an existing class on the fly. A category can be added to any class at runtime, by using the use keyword. To create a category for class, we define a class containing static methods that take as their first parameter an instance of the class that we want to extend. The category can be applied to any closure by using the use keyword denoting a use method, which Groovy adds to java.lang.Object. This use method takes two parameters: a category class and a closure. Listing 3-39 shows how you can add a method to the string class.

Listing 3-39.  Using Categories

1.  class StringExtended {
2.   static String firstUpper(String self) {
3.    return self[0].toUpperCase() + self[1..self.length() - 1]
4.   }
5.  }
6.  use (StringExtended){
7.   assert "aaaa".firstUpper() == "Aaaa"
8.  }
  • In Listing 3-39, the category method is the static firstUpper() in Line 2, which takes an instance of the class that we want to extend as its parameter. Conventionally this parameter is named as self. So in this case we are extending the class String (which is final, but we are metaprogrammers!).
  • In Line 6, we pass the category class and a closure to the use() method.
  • In Line 7, we invoke the category method on the string.

Runtime Mixins

In Java as well as Groovy:

  • A class can inherit only one other class even though it can implement many interfaces.
  • A class cannot inherit from a final class.

Mixin can solve both these problems. Mixins are designed for sharing features while not modifying any existing behavior of the receiver. Features can build on top of each other and merge and blend with the receiver. This can be best illustrated by example in Listing 3-40.

Listing 3-40.  Using a Run-time Mixin

1.  class A{
2.   String genericfeatureNeededByClassB(){}
3.  }
4.  class B extends A{
5.   String getName() { "class B which already extends class A" }
6.  }
7.  final class C{
8.   static test(A self) { "I'm the final class C  and reused by ${self.name} !" }
9.  }
10. B.mixin C
11. println new B().test()

Note the following:

  • Lines 1–3: Shows class A, which has the method genericfeatureNeededByClassB().
  • Lines 5–7: Shows class B, which needs to extend class A, so now it cannot extend any other class.
  • Lines 9–11: Shows a final class C. So now inheriting from this class is out of question. This class has a test() method.
  • Line 10: Shows mixin syntax for merging classes.
  • Line 11: Shows that test() of final class C is available in class B.

With this we complete the journey through the runtime programming in Groovy. Now that you have grasped the concepts of dynamic programming, let us go back in time—not too far, just to a previous section so we can apply our recently acquired knowledge. We will revisit the code from Listing 3-25 and make it dynamic. For quick reference, Listing 3-25 is repeated here.

Listing 3-25.  E-mail Template-Processing Code

1.  import groovy.text.SimpleTemplateEngine
2.
3.  /**
4.  * Simple User Groovy Bean.
5.  */
6.  class User {
7.   String firstName;
8.   String lastName;
9.  }
10.
11. def emailTemplate = this.class.getResource(“nightlyReportsEmail.gtpl”)
12. def binding = [
13.    “user”: new User(firstName: “Christopher”, lastName: “Judd”),
14.    "date": new Date()
15. ]
16. def engine = new SimpleTemplateEngine()
17. def email = engine.createTemplate(emailTemplate).make(binding)
18. def body = email.toString()
19.
20. println body

The code that we will make dynamic is in bold. The simple user GroovyBean, from Lines 6–9 is used simply to pass data to the template engine; it provides no other value, so it’s a great candidate for an expando object.

Listing 3-41 shows code that creates the user GroovyBean from Listing 3-25 using expandos rather than a concrete class.

Listing 3-41.  Alternative to the User GroovyBean

1.  def user = new Expando()
2.
3.  user.firstName = 'Christopher'
4.  user.lastName = 'Judd'
5.
6.  user.greeting = { greeting ->
7.    "${greeting} ${firstName} ${lastName}"
8.  }
9.
10. assert user.greeting("Hello") == 'Hello Christopher Judd'

Listing 3-41 uses an expando as a replacement for a concrete GroovyBean class. On Line 1, a new instance of the expando is created. On Lines 3 and 4, the firstName and lastName properties are added to the object dynamically and assigned values. Properties are not the only things that you can add. You can also add behaviors using closures. Lines 6–8 create and assign a closure to the greeting that concatenates the greeting parameter with the properties for the first and last names. Finally, line 10 executes greeting.

You can also initialize expandos with properties using the overloaded constructor that takes a map. Listing 3-42 uses this technique to re-implement the template example found on Line 13 in Listing 3-25.

Listing 3-42.  Template Example Using Expandos Instead of User GroovyBean

import groovy.text.SimpleTemplateEngine
def emailTemplate = this.class.getResource("nightlyReportsEmail.gtpl")
def binding = [
  "user": new Expando([ firstName: 'Christopher', lastName:'Judd']),
        "date": new Date()
]
def engine = new SimpleTemplateEngine()
def template = engine.createTemplate(emailTemplate).make(binding)
def body = template.toString()
println body

Notice that Listing 3-42 reduces the amount of code and increases the readability of the earlier template example by replacing the User class definition with expando, which contains the firstName and lastName properties.

Now we refactor Line 11 of Listing 3-25, which seems a little more like Java than Groovy, so we will groovify it.

def emailTemplate = this.class.getResource("nightlyReportsEmail.gtpl")

Listing 3-43 shows how MOP could make this a little more Groovy by adding the getResourceAsText() method to java.lang.Class, which actually loads the file and gets the contents of the file as text rather than just the URL to the file.

Listing 3-43.  Adding the getResourceAsText() Method to java.lang.Class

1.  import groovy.text.SimpleTemplateEngine
2.
3.  Class.metaClass.getResourceAsText = { resource ->
4.    this.class.getResourceAsStream(resource).getText()
5.  }
6.
7.  def emailTemplate = this.class.getResourceAsText('nightlyReportsEmail.gtpl')
8.  def binding = [
9.          "user": new Expando([ firstName: 'Christopher', lastName:'Judd']),
10.         "date": new Date()]
11. def engine = new SimpleTemplateEngine()
12. def template = engine.createTemplate(emailTemplate).make(binding)
13. def body = template.toString()
14.
15. println body

Notice how Line 7 expresses much more explicitly that it is loading the template as text and not as a URL. This is accomplished by extending the final java.lang.Class on Lines 3–5 by accessing the metaClass property and adding the getResourceAsText() method that takes a parameter of resource, which is the name of a file on the classpath. The implementation of this method found on Line 4 uses the getResourceAsStream() technique to load a file as a stream. This is generally safer than using a URL, because not everything is easily addressable with a URL. The closure then finishes by using the getText() method, which Groovy includes in the Groovy JDK on all java.io.InputStreams by means of MOP. Finally, Line 7 shows what a call to the getResourceAsText() method would look like on java.lang.Class. There are additional ways in which you might want to do the same thing and make Listing 3-25 a little more expressive. Listing 3-44 shows another implementation of doing the same thing by adding behavior to java.lang.String, which is also a class marked as final.

Listing 3-44.  Adding the fileAsString() Method to java.lang.String

1. String.metaClass.fileAsString = {
2.   this.class.getResourceAsStream(delegate).getText()
3. }
4.
5. println 'nightlyReportsEmail.gtpl'.fileAsString()

Listing 3-44 begins by adding the fileAsString() method to the metaClass property of the java.lang.String class, similar to the previous example. However, it uses a delegate variable instead of a passed-in parameter. The delegate is a reference to the object instance of which the method was called, which in this case would be the string containing the file name to be loaded. Notice how nicely Line 5 reads. It is almost like reading an English sentence. It is not a mere coincidence, as we will discover in the domain-specific language section, but before that we will examine another form of metaprogramming.

Compile-time Metaprogramming

Compile-time metaprogramming provides the user of a programming language, a mechanism to interact with the compiler to allow the construction of arbitrary program fragments; for instance, to add new features to a language or apply application-specific optimizations. Many of the new Groovy features use compile-time metaprogramming to eliminate redundant or verbose code. Compile-time metaprogramming can be very useful, but is less flexible than runtime metaprogramming and should only be used if you must; that is, only if you cannot find a runtime solution for the problem at hand.

AST Transformations

AST stands for Abstract Syntax Tree: a representation of code as data. When the Groovy compiler compiles Groovy scripts and classes, the Groovy parser(the front end of the compiler) creates an AST before generating the final byte code for the class. The AST is the in-memory representation of the source code, comprising class, method, property, statement, and variables. AST transformation is the capability to add new code (methods and checks) into source code before the final byte code is generated. Groovy ships with many annotations for AST transformations that you can readily use to avoid code repetition.

The objective of an AST transformation is to let developers structure code while focusing more on the business code rather than on boilerplate code or cross-cutting concerns. Groovy offers several approaches to accomplishing AST transformations: using annotations shipped with Groovy for AST transformations, local AST transformation, and global AST transformations.

Built-in AST Transformations

To use the built-in AST transformation feature of Groovy, you just need to annotate a class or a method with the annotations provided by Groovy. You don’t have to know anything about compilers or Groovy internals before using the annotations, which we describe in this section. We will cover the most popular AST transformations annotations provided by Groovy. Using all Groovy built-in AST transformations and writing your own custom transformations is a broad subject and merits a book of its own. A detailed explanation on all built-in transformations can be found on the Groovy site (http://groovy.codehaus.org/Compile-time+Metaprogramming+−+AST+Transformations).

Delegate Transformation

A delegate is a relationship between two classes; for example, one class will contain a reference to another class. Listing 3-45 shows a simple usage of @Delegate that wraps an existing class, delegating all calls to the delegate:

Listing 3-45.  Using @Delegate

1.  class Author{
2.   String authorName
3.  }
4.  class Book {
5.   @Delegate Author author
6.   String title
7.  }
8.  def author= new Author(authorName: "vishal")
9.  def book= new Book(title: "Beginning Groovy,Grails and Griffon", author: author)
10. assert book.title == "Beginning Groovy,Grails and Griffon"
11. assert book.authorName == "vishal"
  • Line 5: @Delegate annotation forms a ‘has a’ relationship. The class Book has a reference to class Author.
  • Lines 8–9: Author is initialized and passed to book.
  • Line 11: Field authorName is accessible from the book object because of the @Delegate annotation.

Immutable Transformation

Immutable objects are ones ’that don’t change after initial creation. Immutability implies the following:

  • The class must be final.
  • Fields must be private and final.
  • equals(), hashCode(), and toString() must be implemented in terms of the fields in order to be able to compare objects or use them as keys in maps.

Immutable transformation allows you to write classes in the shortened form, as illustrated in Listing 3-46.

Listing 3-46.  Adding the getResourceAsText() Method to java.lang.Class

1.  @groovy.transform.Immutable class Book{
2.   String title
3.   Collection authors
4.  }
5.  def book1 = new Book(title:'Beginning Groovy, Grails and Griffon', authors:['Vishal', 'Chris', 'James', 'Joseph'])
6.  def book2 = new Book('Beginning Groovy, Grails and Griffon', ['Vishal', 'Chris', 'James', 'Joseph'])
7.  assert book1 == book2

The @Immutable annotation instructs the compiler to execute an AST transformation, which adds the necessary getters, constructors, equals(), hashCode(), and other helper methods that are typically written when creating immutable classes with the defined properties.

Lazy Transformation

Sometimes, you want to handle the initialization of a field of your class lazily, so that its value is computed only on first use, often because it is time-consuming or memory-expensive to create. In Groovy, you can now use the @Lazy annotation for that purpose:

If you run the code in Listing 3-47, you will get an exception as shown.

Listing 3-47.  Using @Lazy

1.  class Groovy {
2.   @Lazy authors= ['Vishal', 'Christopher', 'Joseph', 'James']
3.  }
4.  def g = new Groovy()
5.  assert  g.dump().contains('Vishal')
Caught: Assertion failed:
assert g.dump<>.contains<'Vishal'>
       ¦ ¦      ¦
       ¦ ¦      false
       ¦ <Groovy@8ee016   $authors=null>
       Groovy@8ee016
Assertion failed:
assert g.dump<>.contains<'Vishal'>
       ¦ ¦      ¦
       ¦ ¦      false
       ¦ <Groovy@8ee016   $authors=null>
       Groovy@8ee016

The power assert notifies us that the authors list is null, even if the authors list in Line 2 is not null.

This happens because of the @Lazy annotation. The @Lazy field annotation delays field instantiation until the time when that field is first used. In Line 5, authors is not initialized due to the @lazy annotation, which is corrected in Listing 3-48.

Listing 3-48.  Using @Lazy Annotation

1.  class Groovy {
2.   @Lazy authors= ['Vishal', 'Christopher', 'Joseph', 'James']
3.  }
4.  def g = new Groovy()
5.  assert  g.authors.size() == 4
6.  assert  g.dump().contains('Vishal')

Logging Transformation

For logging, Groovy includes @Log, @Log4j, @Slf4j, and @Commons. Listing 3-49 illustrates using @Log annotation.

Listing 3-49.  Using @Log

1. @groovy.util.logging.Log
2. class Test {
3. def someMethod() {
4. log.fine(complexMethod())
5. }
6. }
7. new Test().someMethod ()
  • Line 1:The @Log annotation first creates a logger based on the name of the class.
  • Line 4: The log method is wrapped with a conditional check whether FINE level is enabled before trying to execute the complexMethod().

The result is equivalent to wrapping the logging call in the following condition:

if (logger.isEnabled(LogLevel.FINE))

Newify Transformation

The @Newify transformation proposes two new ways of instantiating classes.

Listing 3-50 shows a Ruby-like approach to create instances with a new() class method:

Listing 3-50.  Using @Newify for Ruby-like Approach to Create Instances

@Newify rubyLikeNew() {
 assert Integer.new(42) == 42
}

rubyLikeNew()

Listing 3-51.  Creating a tree

class Tree {
    def elements
    Tree(Object... elements) { this.elements = elements as List }
}

class Leaf {
    def value
    Leaf(value) { this.value = value }
}

def buildTree() {
    new Tree(new Tree(new Leaf(1), new Leaf(2)), new Leaf(3))
}

buildTree()

The creation of the tree in Listing 3-51 is not very readable because of all those new keywords spread across the line. The Ruby approach in this case would not be more readable either, since a new() method call for creating each element is needed. But by using @Newify, we can improve our tree building slightly for visual clarity, using ’Python’s approach by omitting the new keyword altogether. Listing 3-52 illustrates a Python-like approach to create instances.

Listing 3-52.  Using @Newify for Python-like Approach to Create Instances

@Newify([Tree, Leaf]) buildTree() {
    Tree(Tree(Leaf(1), Leaf(2)), Leaf(3))
}

Singleton Transformation

We’re used to creating a private constructor, a getInstance() method for a static field, or even an initialized public static final field. The singleton pattern requires a private constructor, and a public static method to access the single instance.

So instead of writing Java code, as illustrated in Listing 3-53, you just need to annotate your type with the @Singleton annotation, as shown in Listing 3-54:

Listing 3-53.  A Singleton Class

public class T {
    public static final T instance = new T();
    private T() {}
}

Listing 3-54.  Using @Singleton

@Singleton class T {}

The singleton instance can then simply be accessed with T.instance (direct public field access).

You can also have the lazy loading approach, shown in Listing 3-55, with an additional annotation parameter:

Listing 3-55.  Using @Singleton with Lazy Loading

@Singleton(lazy = true) class T {}

The code in Listing 3-55 becomes more or less equivalent to the Groovy class created in Listing 3-56:

Listing 3-56.  Groovy Class representationof @Singleton

class T {
    private static volatile T instance
    private T() {}
    static T getInstance () {
        if (instance) {
            instance
        } else {
            synchronized(T) {
                if (instance) {
                    instance
                } else {
                    instance = new T ()
                }
            }
        }
    }
}

Domain-Specific Languages

The dichotomy of generic and specific manifests itself in the programming sphere. Domain-specific languages (DSLs) are one of the forms of the manifestations of this dichotomy. Domain-specific languages are just what they are called: domain specific.

All programming languages are domain-specific languages when they come into existence, but that changes as they evolve. Domain-specific languages are created to solve problems in a certain area (or more precisely, in a certain domain) but as they gradually evolve to solve problems in several domains, the line that distinguishes them as specific blurs. Thus, such a language transgresses from the specific to the generic.

DSLs are focused on the domain or problem and can be of external or internal types.

An external DSL defines a new language with its own custom grammar and parser. An internal DSL defines a new language as well, but within the syntactical boundaries of another language. No custom parser is necessary for internal DSLs. Instead, they are parsed just like any other code written in the language.

Ant, which uses XML, is an example of an external DSL. Gant, on the other hand, uses Groovy to solve the same problem and is an example of an internal DSL. Groovy with its metaprogramming capabilities and flexible syntax is better suited to designing and implementing internal DSLs.

As an illustration, using Groovy’s optional parameters and MOP, you can turn this code that only a programmer can love:

println this.class.getResourceAsStream('readme.txt').getText()
into:
write 'readme.txt'.contents()

Notice that with the second option, even a nonprogrammer has a chance of understanding the intent of the code.

Listing 3-57 shows how to implement this simple DSL for writing files.

Listing 3-57.  Implementation of a Simple DSL

1.  String.metaClass.contents = {
2.    this.class.getResourceAsStream(delegate).getText()
3.  }
4.
5.  def write = { file ->
6.    println file
7.  }
8.
9.  write 'readme.txt'.contents()

Lines 1–3 use the same metaprogramming implementation from the previous section to add a contents closure to the String class. The contents closure loads a file from the classpath as text based on the value of the String. Lines 5–7 implement a closure named write that simply does a println on whatever is passed as a parameter. This ultimately enables Line 9 to read like a sentence when the optional parentheses for the write call are not included.

Summary

Combined with the Groovy topics from the previous chapters, the advanced Groovy topics in this chapter—such as unit testing, XML processing, templates, expandos, and metaprogramming—have prepared you for developing web-based and desktop-based applications using the Groovy-based Grails and Griffon framework that is the focus of the remainder of the book.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset