Book Image

Groovy for Domain-Specific Languages, Second Edition

By : Fergal Dearle
Book Image

Groovy for Domain-Specific Languages, Second Edition

By: Fergal Dearle

Overview of this book

Table of Contents (20 chapters)
Groovy for Domain-specific Languages Second Edition
Credits
About the Author
Acknowledgments
About the Reviewers
www.PacktPub.com
Preface
Free Chapter
1
Introduction to DSLs and Groovy
Index

Groovy


In the later chapters of this book, we will discuss the Groovy language in detail, but let's begin with a brief introduction to the language and some of the features that make it a useful addition to the Java platform.

The Java platform has expanded over the years to cover almost all conceivable application niches—from Enterprise applications, to mobile and embedded applications. The core strengths of Java are its rich set of APIs across all of these problem domains and its standardized virtual machine (VM) interface. The standard VM interface has meant that the promise of "write once, run anywhere" has become a reality. The JVM has been implemented on every hardware architecture and operating system from the mightiest mainframe down to the humble Lego Mindstorms robotic kits for kids.

On top of this standard VM, the list of APIs that have been built extends into every conceivable domain. In addition to the standard APIs that are a part of JME, JSE, and JEE, which are extensive in themselves, there are literally thousands of open source component libraries and tools to choose from. All of this makes for a compelling argument for using Java for almost any software project that you can think of.

For many years of its evolution, the JVM was considered to be just that—a virtual machine for running Java programs. The JVM spec was designed originally by James Gosling to be used exclusively for the Java language. In recent years, there have been a number of open source projects that have started to introduce new languages on top of the JVM, such as JRuby (an implementation of the Ruby language), Jython (an implementation of the Python language and Groovy), Clojure, and Scala.

A natural fit with the JVM

Groovy differs from the preceding languages, as the Groovy language was designed specifically to be a new language to run on the JVM. Groovy is designed to be source compatible with the Java language as well as being binary-compatible at the byte code level.

James Strachan and Bob McWhirter started the Groovy project in August 2003 with the goal of providing a new dynamic and object-oriented language, which can run on the JVM. It took several existing dynamic languages, such as Ruby, Python, Dylan, and Smalltalk, as its inspiration. James had looked at the Python scripting language and had been impressed with the power that it had over Java. James and Bob wanted to design a language that had the powerful scripting features of Python, but stayed as close to the Java language as possible in terms of its syntax.

Groovy is code compatible with Java, and for this reason, it is possible in most cases to take an existing .java file and rename it to .groovy and it will continue to work. Groovy has its own compiler, groovyc, which generates Java byte code from Groovy source files just as the javac compiler does. Groovyc generates class files, which run directly on the JVM. Methods defined in a Groovy class can be called directly from Java and vice versa.

Groovy classes and interfaces are 100 percent binary compatible with their Java counterparts. Uniquely, this means that we can create a new Groovy class that extends a Java class or implements a Java interface. You can also create Java classes that extend Groovy classes or implement Groovy interfaces.

Groovy language features

Groovy adds a number of unique features that distinguish it from Java and allow developers to code at a higher level, and use a more abstract idiom, than is possible with Java. Placing all of these powerful features on top of a language that is code and API compatible with the Java platform is a powerful proposition.

Static and optional typing

In Java, as in other statically-typed languages, variables must first be declared with a type before they can have a value assigned to them. In Groovy, type can be left to be determined at the time of assignment. Groovy supports both static and optional typing as follows:

String str1 = "I'm a String"
def str2 = "I'm also a String"

Both variables str1 and str2 are of the type String. The late binding of the type in the Groovy-style assignment allows for a much less verbose code.

Native support for lists and maps

One of the great bugbears of the Java language is the cumbersome interfaces required for list and map manipulation. Groovy adds native support for all of the Java collection types through a very intuitive and readable syntax. The following code:

def authors =  [ 'Shakespeare', 'Beckett', 'Joyce', 'Poe' ]
println authors
println authors[2]

Produces this output:

[Shakespeare, Beckett, Joyce, Poe]
Joyce

Maps are also declared with ease:

def book = [ fileUnder: "Software Development",
         title: "Groovy for DSL" , author: "Fergal Dearle"]
println book
println book['title']
println book.title

This produces the following output:

[fileUnder: Software Development, title: Groovy for DSL, author: Fergal Dearle]
Groovy for DSL
Groovy for DSL

Closures

Closures are one of the most powerful language features in Groovy. Closures are anonymous code fragments that can be assigned to a variable. Closures can be invoked by the call method as follows:

def biggest = { number1, number2 ->
  number1<number2?number2:number1 
}
// We can invoke the call method of  the Closure class
def result = biggest.call(7, 1)
println result
// We can use the closure reference as if it were a method
result = biggest(3, 5)
println result
// And with optional parenthesis
result = biggest 13, 1
println result

Closures can contain multiple statements and can therefore be as complex as you like. In the following example, we iterate through a list looking for the biggest number, and return it when we are done:

def listBiggest = { list ->
    def biggest = list[0]
    for( i in list) 
        if( i > biggest) 
            biggest = i    
    return biggest
}
def numberList = [ 8, 6, 7, 5, 3, 9]
println listBiggest( numberList) 

Groovy operator overloading

Operator overloading is a powerful feature of the C++ language. Java inherited many of the features of the C++ language, but operator overloading was significantly left out. Groovy introduces operator overloading as a base language feature.

Any Groovy class can implement a full set of operators by implementing the appropriate corresponding method in the class. For example, the plus operator is implemented via the plus() method.

Regular expression support

Groovy builds regular expression handling right into the language via the =~ operator and matcher objects. The following example creates a regular expression to match all multiple occurrences of the space character. This creates a matcher object from this expression and applies it to a string by using the replaceAll method:

def lorem = 
"Lorem ipsum dolor sit amet, consectetur adipisicing elit"
println lorem
def matcher = lorem =~ " +"
def removed = matcher.replaceAll(" ")
println removed

Optional syntax

Optional typing means that variable type annotations are optional. This does not mean that variables have an unknown variable type. It means that the type will be determined at run time based on the value that gets assigned to the variable. All of the following are legal syntax in Groovy:

int a = 3 
def b = 2
String t = "hello" 
def s = 'there'

Trailing semicolons at the end of statements are optional. The only time that you explicitly need to use a semicolon in Groovy is to separate statements that occur on the same line of code, as shown in the first and third lines in the following code:

int a = 3; int b = 4;
def c = 2 
def d = 5; def e = 6

Method call parentheses are also optional when the method being invoked has passed some parameters. We saw earlier, with closures, that we can invoke a closure through its reference as if it were a method call. When invoking a closure in this way, we can also drop the parentheses when passing parameters, as shown in the following code:

println( a );
c = 2
print c
printit = { println it }
printit c

These make for a much looser programming style, which is closer to the scripting syntax of Ruby or Python. This is a big benefit when we are using Groovy to build DSLs. When our target audience is nontechnical, being able to drop parentheses and semicolons will make our code much more legible. Consider the following example, where we have two methods, or closures, to get an account by ID and then credit the account with some funds:

Account account = getAccountById( 234 );
creditAccount( account, 100.00 );

With optional types, such as parentheses and semicolons, this can be used to write code that is far more legible to our target audience:

account = getAccountById 234
creditAccount account, 100.00

Groovy markup

There are a number of builder classes built in Groovy. There are markup builders for HTML, XML, Ant build scripts, and for Swing GUI building. Markup builders allow us to write code to build a tree-based structure directly within our Groovy code. Unlike API-based approaches for building structures, the tree-like structure of the resulting output is immediately obvious from the structure of our Groovy markup code. Consider the following XML structure:

<?xml version="1.0"?>
<book>
   <author>Fergal Dearle</author>
   <title>Groovy for DSL</title>
</book>

In Groovy markup, this XML can be generated simply with the following code fragment:

def builder = new groovy.xml.MarkupBuilder()
builder.book {
   author 'Fergal Dearle'
   title 'Groovy for DSL'
}

At first glance, this looks like strange special case syntax for markup. It's not! The structure of this code can be explained through the use of closures and the optional syntax that we've discussed in this chapter. We will go into this in great detail in Chapter 5, Groovy Closures, but it is interesting at this point to see how the clever use of some language features can yield a powerful DSL-like markup syntax.

Breaking down the preceding code a little, we can rewrite it as:

def builder = new groovy.xml.MarkupBuilder()
def closure = {
   author 'Fergal Dearle'
   title 'Groovy for DSL'
}
// pass a closure to book method
builder.book( closure)
// which can be written without parentheses
builder.book closure
// or just inline the closure as a parameter
builder.book {
…
}

In other words, the code between the curly braces is in fact a closure, which is passed to the book method of MarkupBuilder. Parentheses being optional, we can simply declare the closure inline after the method name, which gives the neat effect of seeming to mirror the markup structure that we expect in the output.

Similarly, author and title are just method invocations on MarkupBuilder with the optional parentheses missing. Extending this paradigm a little further, we can decide to have author take a closure parameter as well:

def builder = new groovy.xml.MarkupBuilder()
builder.book {
   author {
                first_name 'Fergal'
                surname 'Dearle'
         }
   title 'Groovy for DSL'
}

This will output the following nested XML structure:

<?xml version="1.0"?>
<book>
   <author>
              <first_name>Fergal</first_name>
              <surname> Dearle</surname>
   </author>
   <title>Groovy for DSL</title>
</book>

Tip

Downloading the example code

You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

The method calls on MarkupBuilder start off by outputting an opening XML tag, after which they invoke the closure if one has been passed. Finally, the XML tag is properly terminated before the method exits. If we analyze what happens in sequence, we can see that book invokes a closure that contains a call to author. Additionally, the author tag contains a closure with calls to first_name, surname, and so on.

Before you go to the Groovy documentation for MarkupBuilder to look for the book, author, and surname methods in MarkupBuilder, let me save you the effort. They don't exist. These are what we call pretend methods. We will see later in the book how Groovy's metaprogramming features allow us to invoke methods on closure that don't really exist, but have them do something useful anyway.

Already, we are seeing how some of the features of the Groovy language can coalesce to allow the structuring of a very useful DSL. I use the term DSL here for Groovy builders because that is essentially what they are. What initially looks like special language syntax for markup is revealed as being regular closures with a little bit of clever metaprogramming. The result is an embedded or internal DSL for generating markup.