# Groovy Development Kit (TBD)

version 2.3.7

## 1. Working with IO (TBD)

## 2. Working with collections (TBD)

## 3. Handy utilities

### 3.1. ConfigSlurper

`ConfigSlurper` is a utility class for reading configuration files defined in the form of Groovy scripts. Like it is the case with Java `*.properties` files, `ConfigSlurper` allows a dot notation. But in addition, it allows for Closure scoped configuration values and arbitrary object types.

    def config = new ConfigSlurper().parse('''
        app.date = new Date()  (1)
        app.age  = 42
        app {                  (2)
            name = "Test${42}"
        }
    ''')

    assert config.app.date instanceof Date
    assert config.app.age == 42
    assert config.app.name == 'Test42'

1 | Usage of the dot notation
2 | Usage of Closure scopes as an alternative to the dot notation

As can be seen in the above example, the `parse` method can be used to retrieve `groovy.util.ConfigObject` instances. The `ConfigObject` is a specialized `java.util.Map` implementation that either returns the configured value or a new `ConfigObject` instance but never `null`.

    def config = new ConfigSlurper().parse('''
        app.date = new Date()
        app.age  = 42
        app.name = "Test${42}"
    ''')

    assert config.test != null   (1)

1 | `config.test` has not been specified yet it returns a `ConfigObject` when being called.

In the case of a dot being part of a configuration variable name, it can be escaped by using single or double quotes.

    def config = new ConfigSlurper().parse('''
        app."person.age"  = 42
    ''')

    assert config.app."person.age" == 42

In addition, `ConfigSlurper` comes with support for `environments`. The `environments` method can be used to hand over a Closure instance that itself may consist of a several sections. Let’s say we wanted to create a particular configuration value for the development environment. When creating the `ConfigSlurper` instance we can use the `ConfigSlurper(String)` constructor to specify the target environment.

    def config = new ConfigSlurper('development').parse('''
      environments {
           development {
               app.port = 8080
           }

           test {
               app.port = 8082
           }

           production {
               app.port = 80
           }
      }
    ''')

    assert config.app.port == 8080

| The `ConfigSlurper` environments aren’t restricted to any particular environment names. It solely depends on the `ConfigSlurper` client code what value are supported and interpreted accordingly.

The `environments` method is built-in but the `registerConditionalBlock` method can be used to register other method names in addition to the `environments` name.

    def slurper = new ConfigSlurper()
    slurper.registerConditionalBlock('myProject', 'developers')   (1)

    def config = slurper.parse('''
      sendMail = true

      myProject {
           developers {
               sendMail = false
           }
      }
    ''')

    assert !config.sendMail

1 | Once the new block is registered `ConfigSlurper` can parse it.

For Java integration purposes the `toProperties` method can be used to convert the `ConfigObject` to a `java.util.Properties` object that might be stored to a `*.properties` text file. Be aware though that the configuration values are converted to `String` instances during adding them to the newly created `Properties` instance.

    def config = new ConfigSlurper().parse('''
        app.date = new Date()
        app.age  = 42
        app {
            name = "Test${42}"
        }
    ''')

    def properties = config.toProperties()

    assert properties."app.date" instanceof String
    assert properties."app.age" == '42'
    assert properties."app.name" == 'Test42'

### 3.2. Expando

The `Expando` class can be used to create a dynamically expandable object. Despite its name it does not use the `ExpandoMetaClass` underneath. Each `Expando` object represents a standalone, dynamically-crafted instance that can be extended with properties (or methods) at runtime.

    def expando = new Expando()
    expando.name = 'John'

    assert expando.name == 'John'

A special case occurs when a dynamic property registers a `Closure` code block. Once being registered it can be invoked as it would be done with a method call.

    def expando = new Expando()
    expando.toString = { -> 'John' }
    expando.say = { String s -> "John says: ${s}" }

    assert expando as String == 'John'
    assert expando.say('Hi') == 'John says: Hi'

### 3.3. Observable list, map and set

Groovy comes with observable lists, maps and sets. Each of these collections trigger `java.beans.PropertyChangeEvent` events when elements are added, removed or changed. Note that a `PropertyChangeEvent` is not only signaling that a certain event has occurred, moreover, it holds information on the property name and the old/new value a certain property has been changed to.

Depending on the type of change that has happened, observable collections might fire more specialized `PropertyChangeEvent` types. For example, adding an element to an observable list fires an `ObservableList.ElementAddedEvent` event.

    def event                                       (1)
    def listener = {
        if (it instanceof ObservableList.ElementEvent)  {  (2)
            event = it
        }
    } as PropertyChangeListener

    def observable = [1, 2, 3] as ObservableList    (3)
    observable.addPropertyChangeListener(listener)  (4)

    observable.add 42                               (5)

    assert event instanceof ObservableList.ElementAddedEvent

    def elementAddedEvent = event as ObservableList.ElementAddedEvent
    assert elementAddedEvent.changeType == ObservableList.ChangeType.ADDED
    assert elementAddedEvent.index == 3
    assert elementAddedEvent.oldValue == null
    assert elementAddedEvent.newValue == 42

1 | Declares a `PropertyChangeEventListener` that is capturing the fired events
2 | `ObservableList.ElementEvent` and its descendant types are relevant for this listener
3 | Registers the listener
4 | Creates an `ObservableList` from the given list
5 | Triggers an `ObservableList.ElementAddedEvent` event

| Be aware that adding an element in fact causes two events to be triggered. The first is of type `ObservableList.ElementAddedEvent`, the second is a plain `PropertyChangeEvent` that informs listeners about the change of property `size`.

The `ObservableList.ElementClearedEvent` event type is another interesting one. Whenever multiple elements are removed, for example when calling `clear()`, it holds the elements being removed from the list.

    def event
    def listener = {
        if (it instanceof ObservableList.ElementEvent)  {
            event = it
        }
    } as PropertyChangeListener

    def observable = [1, 2, 3] as ObservableList
    observable.addPropertyChangeListener(listener)

    observable.clear()

    assert event instanceof ObservableList.ElementClearedEvent

    def elementClearedEvent = event as ObservableList.ElementClearedEvent
    assert elementClearedEvent.values == [1, 2, 3]
    assert observable.size() == 0

To get an overview of all the supported event types the reader is encouraged to have a look at the JavaDoc documentation or the source code of the observable collection in use.

`ObservableMap` and `ObservableSet` come with the same concepts as we have seen for `ObservableList` in this section.