© Adam L. Davis 2019
Adam L. DavisLearning Groovy 3https://doi.org/10.1007/978-1-4842-5058-7_4

4. GDK

Adam L. Davis1 
(1)
New York, NY, USA
 

The GDK (Groovy Development Kit) provides a number of helper methods, operators, utilities, and additional classes.

Some of these are methods added to every Java class, like "each", and some are more obscure.

Collections

Groovy adds tons of helpful methods that allow easier manipulation of collections, arrays, or any Iterable:
  • sort—Sorts the collection (if it is sortable).

  • findAll—Finds all elements that match a closure.

  • collect—An iterator that builds a new collection.

  • inject—Loops through the values and returns a single value (similar to the concept of “reduce”).

  • each—Iterates through the values using the given closure.

  • eachWithIndex—Iterates through with two parameters: a value and an index.

  • find—Finds the first element that returns true when passed to a given a closure.

  • findIndexOf—Finds the first element that matches a closure and returns its index.

  • any—True if any element returns true for the closure (like OR).

  • every—True if all elements return true for the closure (like AND).

  • reverse—Reverses the ordering of elements in a list.

  • first—Gets the first element of a list.

  • last—Returns the last element of a list.

  • tail—Returns all elements except the first element of a list (useful for, e.g., recursive strategies).

Java 8 Streams

Groovy adds support to simplify dealing with Java 8+ Streams as well. For example, toList() and toSet() are added to the java.util.stream.Stream<T> interface allowing you to shortcut collect(Collectors.toList()) and collect(Collectors.toSet()), respectively.

Spread

The spread operator can be used to access the property of every element in a collection. It can be used instead of collect in many cases. For example, let’s take a simple Dragon class defined as follows:
class Dragon { String name }
You could print the name of every Dragon in a list named dragons:
1   dragons*.name.each { println it }
This is a shorter form for the following:
1   dragons.collect { dragon -> d.name }.each { println it }

GPath

GPath is something like XPath in Groovy. Thanks to the support of property notation for both lists and maps, Groovy provides syntactic sugar, making it really easy to deal with nested collections, as illustrated in the following examples:
1   def  listOfMaps = [['a': 11, 'b': 12], ['a': 21, 'b': 22]]
2   assert listOfMaps.a == [11, 21] //GPath notation
3   assert listOfMaps*.a == [11, 21] //spread dot notation
4
5   listOfMaps = [['a': 11, 'b': 12], ['a': 21, 'b': 22], null]
6   assert  listOfMaps*.a == [11, 21, null] // caters  for  null  values
7   assert listOfMaps*.a == listOfMaps.collect { it?.a } //equivalent  notation
8   // But this will only collect non-null values
9   assert listOfMaps.a == [11,21]

As demonstrated in the preceding examples, *. provides null values, whereas using only “ . ” skips any null values.

IO

The GDK helps you a lot with input/output (IO).

Files

The GDK adds several methods to the File class to ease reading and writing files.
1   println path.toFile().text
A getText() method is added to the File class, which simply reads the whole file.
1   new  File("books.txt").text = "Modern Java"
Here we are using the setText method on the File class, which simply writes the file contents. For binary files, you can also use the bytes property on File:
1   byte[] data = new   File('data').bytes
2   new  File('out').bytes = data
If you want to use an InputStream or reader or the corresponding OutputStream or writer for output, you have the following methods which also handle closing the stream for you:
1   new File('dragons.txt').withInputStream {in -> }
2   new  File('dragons.txt').withReader {r -> }
3   new  File('dragons.txt').withOutputStream {out ->}
4   new  File('dragons.txt').withWriter {w -> }
Lastly you can use the eachLine method to read each line of a file. For example:
1   new  File('dragons.txt').eachLine { line->
2     println "$line"
3   }
4   //OR
5   new  File('dragons.txt').eachLine { line, num ->
6     println "Line $num: $line"
7   }

In all of these cases, Groovy takes care of closing the I/O resource even if an exception is thrown.

../images/426440_2_En_4_Chapter/426440_2_En_4_Figa_HTML.gif Exercise

Print out a multiline file and then read it back in and print out the lines.

URLs

The GDK makes it extremely simple to execute a URL.

The following Java code opens an HTTP connection on the given URL (http://google.com in this case), reads the data into a byte array, and prints out the resulting text.
 1   URL url = new  URL("http://google.com");
 2   InputStream input = (InputStream) url.getContent();
 3   ByteArrayOutputStream out = new  ByteArrayOutputStream();
 4   int n = 0;
 5   byte[] arr = new byte[1024];
 6
 7   while  (-1 != (n = input.read(arr)))
 8   out.write(arr, 0, n);
 9
10   System.out.println(new String(out.toByteArray()));
However, in Groovy this also can be reduced to one line (leaving out exceptions):
1   println "http://google.com".toURL().text

A toURL() method is added to the String class, and a getText() method (which is called using ".text") is added to the URL class in Groovy.

../images/426440_2_En_4_Chapter/426440_2_En_4_Figb_HTML.gif Exercise

Use Groovy to download your favorite web site and see if you can parse something from it.

Ranges

The Range is a built-in type in Groovy. It can be used to perform loops, in switch cases, extracting substrings, and other places. Ranges are generally defined using the syntax start..end.

Ranges come in handy for traversing using the each method and for loops:
1   (1..4).each {print it} //1234
2   for (i in 1..4) print i //1234
A case statement was demonstrated in an earlier chapter, such as the following:
switch (x) {
      case "foo": result = "foo"
            break
      case  12..30: result = "12 to 30"
            break

../images/426440_2_En_4_Chapter/426440_2_En_4_Figc_HTML.gif Warning

This works only if the value given to the switch statement is the same type as the Range (an Integer in this case).

You can use ranges to extract substrings from a string using the getAt syntax. For example:
1   def  text = 'learning groovy',
2   println text[0..4] //learn
3   println text[0..4, 8..-1] //learn groovy

../images/426440_2_En_4_Chapter/426440_2_En_4_Figd_HTML.gif Tip

Negative numbers count down from the last element of a collection or string. So -1 equates to the last element.

You can also use ranges to access elements of a list:
1   def list = ['hank', 'john', 'fred']
2   println list[0..1] //[hank, john]
You can define a range to be exclusive of the last number by using the ..< operator. For example, another way to print 1234 would be the following:
1   (1..<5).each {print it} //1234

../images/426440_2_En_4_Chapter/426440_2_En_4_Fige_HTML.gif Exercise

Attempt to use a variable in a range. Do you need to surround the variable with parentheses?

Utilities

The GDK adds several utility classes, such as ConfigSlurper, JsonBuilder, JsonSlurper, Expando, and ObservableList/Map/Set.

ConfigSlurper

ConfigSlurper is a utility class for reading configuration files defined in the form of Groovy scripts. Like with Java Properties files (files ending with .properties), ConfigSlurper allows for a dot notation. It also allows for nested (closure) configuration values and arbitrary object types.
 1   def config = new ConfigSlurper().parse('''
 2       app.date = new Date()
 3       app.age  = 42
 4       app {
 5           name = "Test${42}"
 6       }
 7   ''')
 8
 9   def properties = config.toProperties()
10
11   assert properties."app.date" instanceof String
12   assert properties."app.age" == '42'
13   assert properties."app.name" == 'Test42'

JsonBuilder and JsonSlurper

Groovy has the JsonBuilder and JsonSlurper classes to help deal with JSON (JavaScript Object Notation), a very common data format. They both are in the “groovy.json” package. For example:
import groovy.json.*
def builder = new JsonBuilder()
builder.person {
    name 'Adam'
    age 37
    conferences 'Gr8Conf', 'ÜberConf'
}
println builder
The preceding code demonstrates using the JsonBuilder to build a person object which would result in the following output:
{"person":{"name":"Adam","age":37,"conferences":["Gr8Conf"," ÜberConf"]}
Likewise we can use the JsonSlurper to parse JSON as follows:
def slurper = new JsonSlurper()
def result = slurper.parseText(builder.toString())
assert result.person.name == "Adam"
assert result.person.age == 37
assert result.person.conferences.size() == 2
assert result.person.conferences[0] == "Gr8Conf"

This parses the same JSON from the previously defined builder and verifies that the returned values are as expected. Note that “result” is a map of values, “person” is also a map, and “conferences” is a list in this case.

Expando

The Expando class can be used to create a dynamically expandable object. You can add fields and methods. This can be useful when you want to use extremely dynamic meta-programming. For example, see the following code:
1   def  expando = new  Expando()
2   expando.name = 'Draco' // field value
3   expando.say = { String s -> "${name} says $s" } //method
4   expando.say('hello') // Output: Draco says hello

../images/426440_2_En_4_Chapter/426440_2_En_4_Figf_HTML.gif Exercise

Use meta-programming to alter some class’s metaClass and then print out the class of the metaClass. Is it the Expando class?

ObservableList/Map/Set

Groovy comes with observable lists, maps, and sets. Each of these collections triggers PropertyChangeEvent (from the java.beans package) when elements are added, removed, or changed. Note that a PropertyChangeEvent does not only signal that an event has occurred, it also holds information on the property name and the old/new value of a property.

Here’s an example using ObservableList and printing out the class of each event:
1   def  list = new  ObservableList()
2   def printer = {e -> println e.class}
3   list.addPropertyChangeListener(printer)
4   list.add 'Harry Potter'
5   list.add 'Hermione Granger'
6   list.remove(0)
7   println list
This would result in the following output:
1   class groovy.util.ObservableList$ElementAddedEvent
2   class java.beans.PropertyChangeEvent
3   class groovy.util.ObservableList$ElementAddedEvent
4   class java.beans.PropertyChangeEvent
5   class groovy.util.ObservableList$ElementRemovedEvent
6   class java.beans.PropertyChangeEvent
7   [Hermione Granger]

This can be useful for using the Observer pattern on collections.

../images/426440_2_En_4_Chapter/426440_2_En_4_Figg_HTML.gif Exercise

Use an ObservableMap and a PropertyChangeListener to reject null values from being added to the map.

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

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