Search

Dark theme | Light theme
Showing posts with label Groovy:Goodness. Show all posts
Showing posts with label Groovy:Goodness. Show all posts

October 9, 2023

Groovy Goodness: Using NullCheck Annotation To Prevent NullPointerException

In Groovy we can apply the @NullCheck annotation to a class, constructor or method. The annotation is an AST (Abstract Syntax Tree) transformation and will insert code that checks for null values in methods or constructors. If a null value is passed to an annotated method or constructor, it will throw an IllegalArgumentException. Without the annotation we could have a NullPointerException if we try to invoke a method on the value we pass as argument. The annotation has an optional property includeGenerated which by default is false. If we set it to true then the null checks are also applied to generated methods and constructors. This is very useful if we apply other AST transformations to our class that generates additional code.

In the following example we use the @NullCheck annotation for a method and at class level:

import groovy.transform.NullCheck

@NullCheck
String upper(String value) {
    "Upper:" + value.toUpperCase()
}

assert upper("groovy") == "Upper:GROOVY"

try {
    upper(null)
} catch (IllegalArgumentException e) {
    assert e.message == "value cannot be null"
}
import groovy.transform.NullCheck

// Apply null check for all constructors and methods.
@NullCheck
class Language {
    private String name

    Language(String name) {
       this.name = name;
    }

    String upper(String prefix) {
        return prefix + name.toUpperCase();
    }
}


def groovy = new Language("groovy")

assert groovy.upper("Upper:") == "Upper:GROOVY"

// Method arguments are checked.
try {
    groovy.upper(null)
} catch (IllegalArgumentException e) {
    assert e.message == "prefix cannot be null"
}

// Constructor argument is also checked.
try {
    def lang = new Language(null)
} catch (IllegalArgumentException e) {
    assert e.message == "name cannot be null"
}

In the following example we set the includeGenerated property to true to also generate null checks for generated code like the constructor generated by @TupleConstructor:

import groovy.transform.NullCheck
import groovy.transform.TupleConstructor

@NullCheck(includeGenerated = true)
@TupleConstructor
class Language {
    final String name

    String upper(String prefix) {
        return prefix + name.toUpperCase();
    }
}

// Constructor is generated by @TupleConstructor and
// @NullCheck is applied to the generated constructor.
try {
    def lang = new Language(null)
} catch (IllegalArgumentException e) {
    assert e.message == "name cannot be null"
}

Written with Groovy 4.0.13

April 21, 2023

Groovy Goodness: Sorting Data With GINQ

GINQ (Groovy-INtegerate Query) is part of Groovy since version 4. With GINQ we can use SQL-like queries to work with in-memory data collections. If we want to sort the data we can use orderby followed by the property of the data we want to sort just like in SQL we can use order by. By default the sort ordering is ascending and null values are put last. We can change the sort ordering by specifying in desc with the orderby clause. Or to make the ascending order explicitly we use the statement in asc. Each of asc and desc also can take an argument to specify how we want null values to be sorted. The default way is to keep null values last in the ordering. If we want to make this explicit we use nullslast as argument to asc or desc. To have null values in the sorted result first we use the argument nullsfirst.

The following example shows all use cases for using orderby when using GINQ:

import groovy.json.JsonSlurper

// Parse sample JSON with a list of users.
def json = new JsonSlurper().parseText('''[
{ "username": "mrhaki", "email": "mrhaki@localhost" },
{ "username": "mrhaki", "email": "user@localhost" },
{ "username": "hubert", "email": "user@localhost" },
{ "username": "hubert", "email": "hubert@localhost" },
{ "username": "hubert", "email": null }
]''')

// Helper method to return a String
// representation of the user row.
def formatUser(row) {
    row.username + "," + row.email
}

// Default ordering is ascending.
// We specify the field name we want to order on.
assert GQ {
    from user in json
    orderby user.username
    select formatUser(user)
}.toList() == [
    'hubert,user@localhost',
    'hubert,hubert@localhost',
    'hubert,null',
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost'
]

// We can explicitly set ordering to ascending.
assert GQ {
    from user in json
    orderby user.email in asc
    select formatUser(user)
}.toList() == [
    'hubert,hubert@localhost',
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost',
    'hubert,user@localhost',
    'hubert,null'
]

// By default null values are last.
// We can also make this explicit as
// option to in asc() or in desc().
assert GQ {
    from user in json
    orderby user.email in asc(nullslast)
    select formatUser(user)
}.toList() == [
    'hubert,hubert@localhost',
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost',
    'hubert,user@localhost',
    'hubert,null'
]

// We can combine multiple properties to sort on.
assert GQ {
    from user in json
    orderby user.username, user.email
    select formatUser(user)
}.toList() == [
    'hubert,hubert@localhost',
    'hubert,user@localhost',
    'hubert,null',
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost'
]

// To order descending we must specify it
// as in desc.
assert GQ {
    from user in json
    orderby user.username in desc
    select formatUser(user)
}.toList() == [
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost',
    'hubert,user@localhost',
    'hubert,hubert@localhost',
    'hubert,null'
]

// We can mix the ordering and set it
// differently for each property.
assert GQ {
    from user in json
    orderby user.username in asc, user.email in desc
    select formatUser(user)
}.toList() == [
    'hubert,user@localhost',
    'hubert,hubert@localhost',
    'hubert,null',
    'mrhaki,user@localhost',
    'mrhaki,mrhaki@localhost'
]

// By default all null values are last,
// but we can use nullsfirst to have null
// values as first value in the ordering.
assert GQ {
    from user in json
    orderby user.username in asc, user.email in desc(nullsfirst)
    select formatUser(user)
}.toList() == [
    'hubert,null',
    'hubert,user@localhost',
    'hubert,hubert@localhost',
    'mrhaki,user@localhost',
    'mrhaki,mrhaki@localhost'
]

Written with Groovy 4.0.11.

Groovy Goodness: Calculate The Median Of A Collection

Since Groovy 4 we can use SQL like queries on in-memory collections with GINQ (Groovy-Integrated Query). GINQ provides some built-in aggregate functions like min, max, sum and others. One of these functions is median. With median we can get the value that is in the middle of the sorted list of values we want to calculate the median for. If the list has an uneven number of elements the element in the middle is returned, but if the list has an even number of elements the average of the two numbers in the middle is returned.

In the following example we see the use of the median function with GINQ:

// List of uneven number of response times.
def responseTimes = [201, 200, 179, 211, 350]

// Get the median from the list of response times.
// As the list has an uneven number of items
// the median is in the middle of the list after
// it has been sorted.
assert GQ {
    from time in responseTimes
    select median(time)
}.toList() == [201]

// List of even number of response times.
responseTimes = [201, 200, 179, 211, 350, 192]

// 2 numbers are the median so the result
// is the average of the 2 numbers.
assert GQ {
    from time in responseTimes
    select median(time)
}.findResult() == 200.5

// Use the GQ annotation and return a List from the method.
@groovy.ginq.transform.GQ(List)
def medianSize(List<String> values) {
    from s in values
    // We can also use an expression to get the median.
    // Here we take the size of the string values to
    // calculage the median.
    select median(s.size())
}

assert medianSize(["Java", "Clojure", "Groovy", "Kotlin", "Scala"]) == [6]

// Sample data structure where each record
// is structured data (map in this case).
// Could also come from JSON for example.
def data = [
    [test: "test1", time: 200],
    [test: "test1", time: 161],
    [test: "test2", time: 427],
    [test: "test2", time: 411],
    [test: "test1", time: 213]
]

// We want to get each record, but also
// the median for all times belonging to a single test.
// We can use the windowing functions provided by GINQ
// together with median.
def query = GQ {
    from result in data
    orderby result.test
    select result.test as test_name,
           result.time as response_time,
           (median(result.time) over(partitionby result.test)) as median_per_test
}

assert query
        .collect { row -> [name: row.test_name,
                           response: row.response_time,
                           median: row.median_per_test] } ==
[
    [name: "test1", response: 200, median: 200],
    [name: "test1", response: 161, median: 200],
    [name: "test1", response: 213, median: 200],
    [name: "test2", response: 427, median: 419],
    [name: "test2", response: 411, median: 419]
]

Written with Groovy 4.0.11.

April 13, 2023

Groovy Goodness: Using Tuples

Groovy supports a tuple type. A tuple is an immutable object to store elements of potentially different types. In Groovy there is a separate Tuple class based on how many elements we want to store in the tuple. The range starts at Tuple0 and ends with Tuple16. So we can store a maximum of 16 elements in a Groovy tuple.
Each of the classes has a constructor that takes all elements we want to store. But the Tuple class also has static factory methods to create those classes. We can use the tuple method and based on how many elements we provide to this method we get the corresponding Tuple object.

To get the elements from a Tuple instance we can use specific properties for each element. The first element can be fetched using the v1 property, the second element is v2 and so on for each element. Alternatively we use the subscript operator ([]) where the first element is at index 0.

Each Tuple instance also has a subList and subTuple method where we can provide the from and to index values of the elements we want to be returned. The methods return a new Tuple with only the elements we requested.
A Groovy tuple is also a List and that means we can use all collection methods for a List also on a Tuple instance.

In the following example we create some tuples and use different methods:

// Using the constructor to create a Tuple.
def tuple2 = new Tuple2("Groovy", "Goodness")

// We can also use the static tuple method.
// Maximum number of elements is 16.
def tuple3 = Tuple.tuple("Groovy", "is", "great")

assert tuple3 instanceof Tuple3


// We can mix types as each elements can
// have it's own type.
def mixed = Tuple.tuple(30, "minutes")

// We can use the subscript operator ([])
// to get a value.
assert mixed[0] == 30
assert mixed[1] == "minutes"

// Or use the get() method.
assert mixed.get(0) instanceof Integer
assert mixed.get(1) instanceof String

// Or use the getter/property V1/V2.
// For each element in a Tuple we can use that.
// Notice that the first element starts with v1.
assert mixed.v1 == 30
assert mixed.getV2() == "minutes"

// Or use multiple assignments.
def (int minutes, String period) = mixed
assert minutes == 30
assert period == "minutes"


// We can get the size.
assert mixed.size() == 2


// Or transform the elements to an array
// and type information is saved.
assert mixed.toArray() == [30, "minutes"]

assert mixed.toArray()[0].class.name == "java.lang.Integer"
assert mixed.toArray()[1].class.name == "java.lang.String"


// Sample tuple with 4 elements.
Tuple4 tuple4 = Tuple.tuple("Groovy", "rocks", "as", "always")

// We can use subList or subTuple to create a new Tuple
// with elements from the original Tuple.
// We need to specify the "from" and "to" index.
// The "to" index is exclusive.
assert tuple4.subList(0, 2) == Tuple.tuple("Groovy", "rocks")
assert tuple4.subTuple(0, 2) == Tuple.tuple("Groovy", "rocks")

// As Tuple extends from List we can use all
// Groovy collection extensions.
assert tuple4.findAll { e -> e.startsWith("a") } == ["as", "always"]
assert tuple4.collect { e -> e.toUpperCase() } == ["GROOVY", "ROCKS", "AS", "ALWAYS"]


// We can even create an empty Tuple.
assert Tuple.tuple() instanceof Tuple0

Written with Groovy 4.0.11.

April 4, 2023

Groovy Goodness: Using Subscript Operator With Multiple Fields On Date Related Objects

Since Groovy 4.0.5 we can use a subscript operator that accepts multiple fields on a java.util.Date and java.util.Calendar objects. And Groovy 4.0.6 extended this subscript operator to any java.time.TemporalAccessor instance. Before these Groovy version we could already use the subscript operator, but we could provide only one field we wanted to access. In a previous post we already have seen this. But now we can use multiple fields to get their values with one statement. We simply define the fields we want as arguments to the subscript operator. Under the hood the subscript operator is implemented by a getAt method that is added as an extension to the Date, Calendar and TemporalAccess classes. The return type is java.util.List and we can combine this with the multiple assignment support in Groovy. In other languages it is also called destructurizing. With multiple assignments we can assign the values from a java.util.List directly to variables.

In the following example we see several usages of the subscript operator with multiple fields on Date, Calendar and LocalDateTime objects:

import java.time.LocalDateTime
import java.time.Month
import static java.time.temporal.ChronoField.*
import static java.time.temporal.IsoFields.*

// Create a Date instance.
def date = new Date().parse('yyyy/MM/dd', '1973/07/09')

// Groovy adds the subscript operator for multiple
// fields to the Date class.
def output = date[Calendar.DATE, Calendar.MONTH, Calendar.YEAR]
assert output == [9, 6, 1973]

// The result is a list and we can destructurize it
// to assign values to variables (also called multiple assignments).
def (day, month, year) = date[Calendar.DATE, Calendar.MONTH, Calendar.YEAR]

assert "$day-${month + 1}-$year" == "9-7-1973"


// Create a Calendar instance.
def calendar = date.toCalendar()

// The subscript operator supporting multiple fields
// is also added to the Calendar class.
def (calDay, calMonth, calYear) = calendar[Calendar.DATE, Calendar.MONTH, Calendar.YEAR]

assert "Time to celebrate on $calDay-${calMonth + 1}" == "Time to celebrate on 9-7"


// Create a LocalDateTime instance
def birthDateTime = LocalDateTime.of(1973, Month.JULY, 9, 6, 30, 0);

// Groovy adds the subscript operator with multiple fields
// on any TemporalAccessor instance.
def (dayOfWeek, dayOfYear, quarter, week) = birthDateTime[DAY_OF_WEEK, DAY_OF_YEAR, QUARTER_OF_YEAR, WEEK_OF_WEEK_BASED_YEAR]

assert "Born in week $week on day $dayOfWeek" == "Born in week 28 on day 1"
assert quarter == 3
assert dayOfYear == 190

def (hour, minute) = birthDateTime[HOUR_OF_DAY, MINUTE_OF_HOUR]

assert "Live started at $hour:$minute" == "Live started at 6:30"

Written with Groovy 4.0.11.

July 13, 2022

Groovy Goodness: Using Macros For Getting More Information About Variables

In a previous post we learned about the macros SV, SVI and SVD that return a string representation of variables with their name and value. Groovy 4 also added the NP and NPL macros that we can use to inspect variables. Instead of returning a GString instance these macros return a NamedValue instance or a list of NamedValue value instances. The NamedValue class is a simple class with a property name, containing the name of the variable, and property val with the value of the variable. The macro NP can be used when we have a single variable and result is a single NamedValue instance. An the macro NVL can be used with multiple variables. The result is a list of NamedValue instances where each instance represent a variable passed as argument to NVL.

In the following example we use NV and NVL:

def languages = ["Java", "Groovy", "Clojure"]

// Use NV macro to create a NamedValue object 
// with name and val properties containing the
// variable name and variable value.
def namedValue = NV(languages)

assert namedValue.name == "languages"
assert namedValue.val == ["Java", "Groovy", "Clojure"]
assert namedValue.class.name == "groovy.lang.NamedValue"


def alias = "mrhaki"
def name = "Hubert"
def age = 49

// We can pass multiple objects with the NVL macro
// and we get a List with multiple NamedValue objects.
def namedValues = NVL(alias, name, age)
assert namedValues.size() == 3
assert namedValues == [new NamedValue("alias", "mrhaki"), new NamedValue("name", "Hubert"), new NamedValue("age", 49)]

// We can use Groovy collection methods.
assert namedValues[0].name == "alias"
assert namedValues[0].val == "mrhaki"
assert namedValues.name == ["alias", "name", "age"]
assert namedValues.val == ["mrhaki", "Hubert", 49]
assert namedValues.find { nv -> nv.name == "age" }.val == 49

Written with Groovy 4.0.3.

Groovy Goodness: Using Macros For Getting String Representation Of Variables

Groovy 4 added some built-in macros that we can use in our code. A macro is code that will create new code. It does this by manipulating the Abstract Syntax Tree (AST) before the code is compiled. So when we use a macro, the macro will change the AST and those changes will be compiled. The three built-in macros SV, SVI and SVD can create a GString instance with the names and values of the variables that are passed as argument. This can be very useful to create some meaningful debugging statements. Normally we would have to type in the variable name ourselves followed by the value. Now with these macros we don't have to type the variable as the macro will add that to the AST for us.

The SV macro will use the toString method of the variable to get the value. When we want to use the inspect method for the value we can use the SVI macro. Finally we can use the SVD macro that uses the dump method to get the variable value.

In the following example we use all three macros with different variables:

def languages = ["Java", "Groovy", "Clojure"]

// With SV the toString method for the object is used.
// The name of the variable is also in our output.
assert SV(languages) == "languages=[Java, Groovy, Clojure]"
assert SV(languages).class.name == "org.codehaus.groovy.runtime.GStringImpl"

// With SVI the inspect method for the object is used.
assert SVI(languages) == "languages=['Java', 'Groovy', 'Clojure']"
assert SVI(languages).class.name == "org.codehaus.groovy.runtime.GStringImpl"

// We cannot assert here as the output contains the object instance representation
// and that changes with each run.
SVD(languages) // Possible output: languages=<java.util.ArrayList@8f636a77 elementData[Java, Groov, Clojure] size=3 modCount=3>
assert SVI(languages).class.name == "org.codehaus.groovy.runtime.GStringImpl"


// We can pass multiple objects to the SV, SVI and SVD macros.
def alias = "mrhaki"
def name = "Hubert"
def age = 49

assert SV(alias, name, age) == "alias=mrhaki, name=Hubert, age=49"
assert SVI(alias, name, age) == "alias='mrhaki', name='Hubert', age=49"
SVD(alias, name, age) // Possible output: alias=<java.lang.String@c077733c value=[109, 114, 104, 97, 107, 105] coder=0 hash=-1065913540 hashIsZero=false>, name=<java.lang.String@817bc072 value=[72, 117, 98, 101, 114, 116] coder=0 hash=-2122596238 hashIsZero=false>, age=<java.lang.Integer@31 value=49>    

Written with Groovy 4.0.3.

July 4, 2022

Groovy Goodness: Closed And Open Ranges

Groovy supports ranges for a long time. But Groovy 4 adds a new feature for ranges and that is the support for open (exclusive) ranges at the beginning of a range. Open means the number that defines the range is not part of the actual range result and we must use the less-than character (<). This is also referred to as exclusive, where the value is excluded from the range. When a range is closed the value is included, also called inclusive. Before Groovy 4 we could already define the end of the range to be exclusive or inclusive, but now we can also define the beginning of the range to be exclusive.

In the following example we use closed and open range definitions from the start or end:

def inclRange = 0..5

assert inclRange == [0, 1, 2, 3, 4, 5]
assert inclRange.from == 0
assert inclRange.to == 5


def exclEndRange = 0..<5

assert exclEndRange == [0, 1, 2, 3, 4]
assert exclEndRange.from == 0
assert exclEndRange.to == 4


// Support for exclusive begin added in Groovy 4.
def exclBeginRange = 0<..5

assert exclBeginRange == [1, 2, 3, 4, 5]
assert exclBeginRange.from == 1
assert exclBeginRange.to == 5


// Support for exclusive begin added in Groovy 4.
def exclRange = 0<..<5

assert exclRange == [1, 2, 3, 4]
assert exclRange.from == 1
assert exclRange.to == 4

Written with Groovy 4.0.3.

July 1, 2022

Groovy Goodness: Creating TOML Configuration With TomlBuilder

Groovy 4 introduced support for TOML configuration file. In a previous post we already learned how we can parse TOML content. In this post we will see we can use a builder syntax to create TOML content. We need the class TomlBuilder and then define our structure using a nice builder DSL. The DSL is comparable to create JSON using the JsonBuilder. The names of the nodes in the DSL structure will be the names of the properties. Nodes within nodes will result in concatenated property names with the name of each node separated by a dot (.). We can also use collections as arguments and those will translated to TOML arrays. A collection can optionally be followed by a closure that processes each item in the collection to generate the content for the TOML array.

In the following example we use the builder syntax to create what we want in our TOML content. Using the toString method we get the TOML data as string:

import groovy.toml.TomlBuilder

// Helper record class to store "server" properties.
record Server(String env, String host, int port) {}

// Create Tomlbuilder.
def toml = new TomlBuilder()

// Define structure.
toml {
    // Use closure to group.
    application {
        name "Groovy TOML"
        version "1.0.0"
    }

    // Use closures to define levels.
    users {
        env {
           enabled true
        }
        acc {
            enabled false
        }
    }
         
    // Use maps
    debug(enabled: true)  
    
    // We can use collections
    ports([80, 443]) 
    
    // Convert data with closure applied for each item in collection.
    servers([new Server("dev", "localhost", 8080), 
             new Server("uat", "cloud-acc", 80)], { server -> 
        env server.env 
        host server.host 
        port server.port 
    }) 
}

assert toml.toString() == """\
application.name = 'Groovy TOML'
application.version = '1.0.0'
users.env.enabled = true
users.acc.enabled = false
debug.enabled = true
ports = [80, 443]
servers = [{env = 'dev', host = 'localhost', port = 8080}, {env = 'uat', host = 'cloud-acc', port = 80}]
"""

// In order to write to a writer we could use:
// def sw = new StringWriter()
// toml.writeTo(sw)
// def content = sw.toString()

Instead of using the builder DSL syntax we can also use a Map with all data we want to transform to TOML data:

import groovy.toml.TomlBuilder

def instant = Instant.ofEpochSecond(1656487920)
def clock = Clock.fixed(instant, ZoneOffset.UTC)

def config = [
    application: [
      name: "Groovy TOML",
      version: "1.0.0"
    ],
    users: [
        dev: [enabled: true],
        uat: [enabled: false]
    ],
    ports: [80, 443],
    debug: [
        enabled: false
    ],
    build: [
        jdk: 'openjdk version "17.0.3" 2022-04-19',
        time: ZonedDateTime.now(clock).dateTimeString
   ],
   servers: [
       [env: "dev", host: "localhost", port: 8080],
       [env: "uat", host: "cloud-acc", port: 80]
   ]
]

// Create TomlBuilder
def toml = new TomlBuilder()

// Use data defined in the Map.
toml config

assert toml.toString() == """\
application.name = 'Groovy TOML'
application.version = '1.0.0'
users.dev.enabled = true
users.uat.enabled = false
ports = [80, 443]
debug.enabled = false
build.jdk = 'openjdk version "17.0.3" 2022-04-19'
build.time = '2022-06-29T07:32:00Z'
servers = [{env = 'dev', host = 'localhost', port = 8080}, {env = 'uat', host = 'cloud-acc', port = 80}]
"""

Written with Groovy 4.0.3.

June 29, 2022

Groovy Goodness: Reading TOML Configuration

Since Groovy 4 we can parse TOML configuration data into a Map. Once the TOML data is transformed into the Map we can use all possibilities in Groovy to lookup keys and their values in maps. For example we can use GPath expressions to easily get the value of a (nested) key. To parse TOML configuration data we must use the TomlSlurper class that is in the groovy.toml package. We can use the parse method when we have a file, reader or stream with our configuration. To parse a String value with TOML configuration we use the parseText method.

In the following example we define our configuration using TOML syntax and use the parseText method to transform it into a Map. We use different ways with GPath expressions to read the configuration data:

import groovy.toml.TomlSlurper

// Configuration in TOML syntax.
def config = '''
application.name = "Groovy TOML"
application.version = "1.0.0"
application.ports = [80, 443]

# Set to true for debugging
debug.enabled = false

[build]
jdk = 'openjdk version "17.0.3" 2022-04-19'
time = "2022-06-29T07:32:00Z"

[[servers]]
name = "dev"
host = "localhost"
port = 8080

[[servers]]
name = "uat"
host = "cloud-acc"
port = 80
'''

// We get back a Map with all configuration.
def toml = new TomlSlurper().parseText(config)
// To read data from files, readers or streams we can use overloaded
// versions of the method TomlSlurper#parse.

// We can reference the properties using GPath expressions.
assert toml.application.name == "Groovy TOML"
assert toml.application.version == "1.0.0"
// TOML array is transformed to ArrayList.
assert toml.application.ports == [80, 443]
assert toml.application.ports.class == java.util.ArrayList
assert toml.application == [name: "Groovy TOML", version: "1.0.0", ports: [80, 443]]

// TOML boolean is transformed to boolean.
assert !toml.debug.enabled

assert toml.build.jdk == /openjdk version "17.0.3" 2022-04-19/
assert toml.build.time == "2022-06-29T07:32:00Z"
// Dates are not parsed, but we get them as String value.
assert toml.build.time.class == java.lang.String

// Array of tables in TOML are also supported by the TomlSlurper.
assert toml.servers.size() == 2

def developmentConfig = toml.servers.find { s -> s.name == "dev" }
assert developmentConfig.host == "localhost"
assert developmentConfig.port == 8080

def uatConfig = toml.servers.find { s -> s.name == "uat" }
assert uatConfig.host == "cloud-acc"
assert uatConfig.port == 80

assert toml.servers*.name == ["dev", "uat"]

Written with Groovy 4.0.3.

June 28, 2022

Groovy Goodness: Get Row Number In GINQ Result Set

GINQ (Groovy-INtegrated Query) is added since Groovy 4. With GINQ we can query in-memory collections with SQL like statements. If we want to get the row numbers for each row in the query result set we can use the implicit variable _rn. We must specify _rn in the select expression of our GINQ query. We can even use as to give it a meaningful name in the result set.

In the following example we have a basic GINQ query where use _rn to get the row number of each row in the query result set:

def letters = GQ {
    from letter in ['a', 'b', 'c']
    select _rn, letter
}.collect { item -> [item._rn, item.letter] }

assert letters == [[0, 'a'], [1, 'b'], [2, 'c']]

In the following example we first parse JSON and then use GINQ to query it. We see in the example that the row number is based on the result after the where expression is applied:

import groovy.json.JsonSlurper

def json = new JsonSlurper().parseText('''
[
  {
    "id": 1001,
    "language": "Groovy"
  },
  {
    "id": 1002,
    "language": "Clojure"
  }, 
  {
    "id": 1003,
    "language": "Java"
  }
]
''')

def languages = GQ {
  from l in json
  where l.language == "Groovy"
  // We add 1 to _rn to make it 1-based instead of 0-based. 
  // Also we use as rowNumber to give a meaningful name.
  select _rn + 1 as rowNumber, l.id as id, l.language as name
}.collect { item -> [row: item.rowNumber, name: item.name] }

// Also notice the row number is calculated based
// on the result after applying the where clause.
assert languages.first() == [row: 1, name: "Groovy"]

Written with Groovy 4.0.3.

Groovy Goodness: Using The Switch Expression

In a previous (old) post we learned how Groovy supports different classifiers for a switch case statement. Since Groovy 4 we can use switch also as an expression. This means the switch statement returns a value without having to use return. Instead of using a colon (:) and break we use the -> notation for a case. We specify the value that the switch expressions returns after ->. When we need a code block we simply put the code between curly braces ({...}).

In the following example we use the switch expression with different case statements:

def testSwitch(val) {
    switch (val) {
        case 52 -> 'Number value match'
        case "Groovy 4" -> 'String value match'
        case ~/^Switch.*Groovy$/ -> 'Pattern match'
        case BigInteger -> 'Class isInstance'
        case 60..90 -> 'Range contains'
        case [21, 'test', 9.12] -> 'List contains'
        case 42.056 -> 'Object equals'
        case { it instanceof Integer && it < 50 } -> 'Closure boolean'
        case [groovy: 'Rocks!', version: '1.7.6'] -> "Map contains key '$val'"
        default -> 'Default'
    } 
}

assert testSwitch(52) == 'Number value match'
assert testSwitch("Groovy 4") == 'String value match'
assert testSwitch("Switch to Groovy") == 'Pattern match'
assert testSwitch(42G) == 'Class isInstance'
assert testSwitch(70) == 'Range contains'
assert testSwitch('test') == 'List contains'
assert testSwitch(42.056) == 'Object equals'
assert testSwitch(20) == 'Closure boolean' 
assert testSwitch('groovy') == "Map contains key 'groovy'"
assert testSwitch('default') == 'Default'

Written with Groovy 4.0.3.

March 11, 2020

Groovy Goodness: Getting Parts Of A String Enclosed By Strings

Groovy 3 adds the takeBetween method to the String class. With this method we can get all the characters that are enclosed by string values. We can specify one enclosed string value and then all text between the the first occurrence of the string and the second occurrence is returned. If multiple parts are enclosed by the string values we can also specify which occurrence we want. If the text is enclosed by different string values we can use a variant of takeBetween that takes two string values to indicate the boundaries of the text we want. Also with two different enclosed string values we can use an argument to get the n-th occurrence of the string that is found.
Since Groovy 3 we can also use takeBefore and takeAfter to get the string before or after a given string value. All three methods will return an empty string if no text can be found.

In the following example we use the takeBefore, takeAfter and takeBetween methods with different arguments:

def text = 'Just saying: "Groovy is gr8!"'

// Return all characters before the first quote.
assert text.takeBefore('"') == 'Just saying: '
// Return everything after the colon.
assert text.takeAfter(': ') == '"Groovy is gr8!"'
// Return everything between two quotes.
assert text.takeBetween('"') == 'Groovy is gr8!'
// Return text between is and !.
assert text.takeBetween('is', '!') == ' gr8'

// When no value can be found
// an empty string is returned.
assert text.takeBefore('?') == ''
assert text.takeAfter('Java') == ''
assert text.takeBetween('-') == ''
assert text.takeBetween('[', '/') == ''

def sample = 'JVM languages are "Groovy", "Clojure", "Java".'

assert sample.takeBetween('"') == 'Groovy'
// We can also specify which occurrence we 
// want for a text between same strings.
assert sample.takeBetween('"', 0) == 'Groovy'
assert sample.takeBetween('"', 1) == 'Clojure'
assert sample.takeBetween('"', 2) == 'Java'


def users = "Users: [mrhaki], [hubert]"

assert users.takeBetween('[', ']') == 'mrhaki'
// We can also specify which occurrence we 
// want for a text between to strings.
assert users.takeBetween('[', ']', 0) == 'mrhaki'
assert users.takeBetween('[', ']', 1) == 'hubert'
// When no occurrence an empty string is returned.
assert users.takeBetween('[', ']', 2) == ''

Written with Groovy 3.0.2.

March 10, 2020

Groovy Goodness: Taking Or Dropping Number Of Characters From A String

Groovy adds a lot of methods to the Java String class. For example we can use the take method to get a certain number of characters from the start of a string value. With the drop method we remove a given number of characters from the start of the string. In Groovy 3 we can now also take and drop a certain number of characters from the end of a string using the methods takeRight and dropRight.

In the following example we see how we can use the methods:

def s = "Groovy rocks!"

// Drop first 7 characters.
assert s.drop(7) == "rocks!"

// Drop last 7 characters.
assert s.dropRight(7) == "Groovy"


// Take first 6 characters.
assert s.take(6) == "Groovy"

// Take last 6 characters.
assert s.takeRight(6) == "rocks!"

Written with Groovy 3.0.2.

March 8, 2020

Groovy Goodness: Check Object Instances Are The Same With === Operator

Groovy has used the == operator to check if objects are equal for a long time. To test if object instances are the same we must use the is method. Groovy 3 adds a new operator for the is method and that is the === operator. And to negate the result of the is method we can use the !== operator.

In the following example we use the === and !== operator to check if objects refer to the same instance or not:

def answer = new Integer(42)
def anotherAnswer = new Integer(42)
def meaningOfLife = answer

// Equals is true for answer and anotherAnswer
assert answer == anotherAnswer

// But they don't refer to the same instance.
assert answer !== anotherAnswer

// The variables meaningOfLife and answer
// do refer to the same object instance.
assert answer === meaningOfLife

Written with Groovy 3.0.2.

March 4, 2020

Groovy Goodness: Using !instanceof Operator

Groovy 3 adds the !instanceof operator to check if an object is not an instance of a type. This is a shorthand for using instanceof and then negate the result. It shows how little changes can make code easier to read.

In the following example we use the old way to check if object is not an instance of a type and the new !instanceof operator:

// Check using !(obj instanceof type) 
// if object is not an instance of type.
assert !("Groovy is awesome!" instanceof Number)

// Since Groovy 3 we can use !instanceof to check
// if object is not an instance of a type.
assert 42 !instanceof String

Written with Groovy 3.0.1.

March 3, 2020

Groovy Goodness: Safe Index Based Access For Lists, Arrays and Maps

Groovy 3 adds the feature of safe index based access for lists, arrays and maps. This means we can use ?[index] to get or a set a value on a list or array without getting a NullPointerException when the list or array is not initialised. With maps we can use ?[key] for getting a value or set a value and when the map object is not initialised we don't get a NullPointerException.

In the following example we see several examples of setting or getting values using indices or keys:

def list = null

// Accessing null list or array 
// using index based access, 
// will throw NullPointerException when
// collection is null.
try {
    list[0]
} catch (NullPointerException npe) {
    assert npe
}

// Using ?[...] will not throw NullPointerException.
assert list?[0] == null

// Assignment are ignored when list is null.
list?[1] = 42
assert list?[1] == null

// When collection is not null we simply
// get the default behaviour for index based
// access of elements.
def values = ["Groovy", "rocks"]
assert values?[0] == "Groovy"
values?[2] = '!'
assert values?.join(" ") == "Groovy rocks !"

// We can use ?[key] for maps as well.
def info = null
assert info?['address'] == null
info?['address'] = 'unknown'
assert info?['address'] == null

def user = [alias: 'mrhaki', loves: 'Groovy']
assert user?['alias'] == 'mrhaki'
user?['country'] = 'The Netherlands'
assert user?['country'] == 'The Netherlands'
assert user?['age'] == null

Written with Groovy 3.0.1.

February 27, 2020

Groovy Goodness: The Elvis Assignment Operator

Groovy 3 adds a new operator to the language: elvis assignment operator (?=). This operator is a shorthand for an assignment where we want to assign a value to a variable or property if the variable or property is null or false (following Groovy truth). If the value of the variable or property is not null or false (again apply Groovy truth rules), the value stays the same.

In the following example code we use the elvis assignment operator:

// First an example using the elvis operator.
a = null
a = a ?: 100

assert a == 100

// Now we can shorten the assignment using the 
// elvis assignment operator.
a = null
a ?= 100

assert a == 100

b = 200
b ?= 100

assert b == 200

// The elvis assignment operator can also 
// be useful in maps to set default values.
def m = [a: 1, b: 2]
m.a = 100
m.b ?= 200
m.c ?= 300

assert m.a == 100
assert m.b == 2
assert m.c == 300

// Also to set default values for properties
// in an object. Especially if we cannot
// change the class to set a default value.
// In our example we have the source of the
// Email class and we could set default values
// in the class definition, but if the class
// is not under our control we can use the
// elvis assignment operator to set default
// values easily.
class Email {
    String to, from, subject, body
}

def mail = new Email(to: 'test@mrhaki.com', 
                     subject: 'Sample', 
                     body: 'Trying Elvis assignment.')
mail.subject ?= 'Mail has no subject'
mail.from ?= 'no-reply@mrhaki.com'

assert mail.to == 'test@mrhaki.com'
assert mail.from == 'no-reply@mrhaki.com'
assert mail.subject == 'Sample'
assert mail.body == 'Trying Elvis assignment.'

Written with Groovy 3.0.1.

February 23, 2020

Groovy Goodness: Lambda Default Parameter Value

Groovy 3 adds support for Java's lambda syntax expressions. This way we can write code in Groovy using lambda expressions just like in Java. But Groovy adds an additional feature and that is default parameter values for lambda expressions.

In the following example we use a default parameter value for a lambda expression.

// Groovy 3 supports Java's lambda syntax expressions.
def inc = n -> n + 1

assert inc(1) == 2


// But also adds default parameter values.
def multiplyBy = (n, factor = 2) -> n * factor

assert multiplyBy(1) == 2
assert multiplyBy(1, 10) == 10

Written with Groovy 3.0.1.

February 21, 2020

Groovy Goodness: Check Item Is Not In A Collection With !in

Groovy contains lots of little gems that just make live easier and our code more expressive. Groovy 3 doesn't disappoint by adding some nice syntax additions. For example we can now use !in to check if an item is not in a collection, opposed to in that checks if an item is in a collection.

In the following example we use !in:

def list = ['Groovy', 3, 'is', 'awesome', '!']

// New Groovy 3 syntax to check item 
// is NOT in a collection.
assert 'Java' !in list

// Old syntax.
assert !('Java' in list)

// Just to show in still works.
assert 'Groovy' in list

Written with Groovy 3.0.1.