Groovy for testers Apache Groovy

Groovy for testers


I have used Groovy since late 2015. The language is such a pleasure to code in. For me it is like a happy intersection of Ruby (e.g. blocks and other language features), Python (e.g. closures and list comprehension) and Java (Java is legal Groovy). I have used all three those, and other languages, extensively in test automation. Groovy's syntactic sugar makes it effortless to do tasks that are more cumbersome in other languages, especially Java. Yet it has the power and the ease of library incorporation that Java has, or better. Ever tried Ruby gems or Python pip from behind a draconian corporate NTLM proxy? It sucks.

Don't expect Groovy code in the distinguished halls of computer science (not that it stands back for any other JVM language). Instead, you'll often it in the kitchen, happily doing the dirty work. And doing it well. To me, Groovy has become one of the de-facto programming languages for software test automation. It has had Geb and Spock for long, it is the built-in scripting language in SoapUI and of late in JMeter. It plays very well with Selenium, Cucumber and Gherkin. And it is just a good language for the fetch-and-carry sort of forklift work that is required for back-end testing. Interact with queues, databases, filesystems and so on with less effort than Java or Python (IMHO). It is now in the stable of the Apache Foundation, so it has a safe home after a bit of turmoil.

As a side note, after having initially shunned the language, I discovered it's coolness when I was forced to use it in SoapUI. The name Groovy more than anything else, made me shy away from it. Just the other day I did a one-slide system metaphor for a test automation solution, and I was wondering what the executives think when they see Groovy in the core of the metaphor. But now I understand the name. It suits my I'm-more-chilled-because-I'm-over-forty outlook. Don't be like me. If you still need convincing about Groovy, read this article by Paul King.

Now that I have lowered your expectations, here are some out of context snippets that I have found remarkable or useful, which is what this whole site is really about. Or maybe it's just a fan page. I don't care. Also, I'm not going to discuss the best IDE's, how to install the language, where to get libraries and tools, or how to print hello world - you can find that in plenty of other places. The Groovy logo at the top is the only real external link on this page. Click it. I may add more pages focused on other tools in future. For now, I also have a page about SoapUI, a page about getting Groovy, Selenium and Cucumber to work, and a page about VBScript for testers.

I provide the Groovy code for all the examples here as a downloadable Eclipse project (you'll need the groovy-eclipse plugin) and a Netbeans project (you need the Pure Groovy Project plugin).

The four areas where Groovy really works for me every day, are: string manipulation, collection iteration, database interaction and handling xml. here are some examples of those and others.

Kick-off
String manipulation
Closures, ranges and other constructs
Collection iteration
Random numbers and strings
Filesystem interaction
Handling xml
Database work
Graphing with charts
Bonus level (run Python code from Groovy)

Kick-off

This is a way to get your important system variables. For example, you are sitting in front of a PC with SoapUI installed, and want an inventory of what you have. Unfortunately the version of Groovy that ships with SoapUI is normally quite old.
println "os.arch: " + System.getProperty("os.arch")
println "java -version".execute().err.text
println "GroovySystem.version: " + GroovySystem.version
println "java.library.path: " + System.properties["java.library.path"]
Query a specific library:
xmlSlurper = new XmlSlurper()
println xmlSlurper.class.getCanonicalName()
println xmlSlurper.getClass().getPackage().getImplementationTitle()
println xmlSlurper.getClass().getPackage().getImplementationVendor()
println xmlSlurper.getClass().getPackage().getSpecificationVersion()
println xmlSlurper.getClass().getPackage().getImplementationVersion()
println xmlSlurper.class.getProtectionDomain().getCodeSource().getLocation().getPath()
println xmlSlurper.class.getProperties().each { println it }

String manipulation

Test automation is all about pattern matching, replacing values and other ways of manipulating data that is inputted or emitted from systems. For the uninitiated, using assert like this in examples means it is running code, but you can also see the output without actually running it. Neat.
assert 'Groovy'.length() == 6
assert 'Groovy'.substring(3) == 'ovy'
assert 'Groovy'.substring(0,2) == 'Gr'
assert 'Groovy'.take(4) == 'Groo'

assert 'Groovy'.replace('oo', 'a') == 'Gravy' 
assert 'Groovy'.take(3).replace('o', 'u') == 'Gru'
assert 'Groovy'.replaceAll('o', 'a') == 'Graavy'

assert 'Groovy'.toLowerCase() == 'groovy'
assert 'Groovy'.toUpperCase() == 'GROOVY'
assert 'groovy'.capitalize() == 'Groovy'
assert 'grOOvy'.capitalize() == 'GrOOvy'
assert 'grOOvy'.toLowerCase().capitalize() == 'Groovy'
assert 'Groovy'.reverse() == 'yvoorG'

assert 'Groovy'.contains('oo') == true
assert 'o' in 'Groovy'.collect() == true  //it works on Collection not String
assert 'Groovy'.equals('Groovy') == true
assert 'Groovy'.equalsIgnoreCase('gRoOvY') == true
assert 'Groovy'.startsWith('Gr')
assert 'Groovy'.endsWith('vy')
assert 'Groovy'.isEmpty() == false
assert 'Groovy'.indexOf('v') == 4

assert 'Groovy'.matches(/^Gr.*/) == true
assert 'Groovy'.matches(/.*oo.*/) == true
assert 'Groovy'.matches(/.*vy$/) == true
assert 'Groovy'.split('r') == ['G', 'oovy']

assert 'Groovy'.padLeft(10, '*') == '****Groovy'
assert 'Groovy'.padRight(10, '*') == 'Groovy****'
assert 'Groovy'.center(10, '*') == '**Groovy**'

assert 'Groovy' - 'o' == 'Grovy'
assert 'Groovy' + 'Baby' == 'GroovyBaby'
assert 'Groovy' * 2 == 'GroovyGroovy'

assert ' Groovy '.trim() == 'Groovy'
assert 'Groovy '.trim() == 'Groovy'
assert ' Groovy'.trim() == 'Groovy'
Let's make our own left trim and right trim as an exercise in closures:
def ltrim = { it.replaceAll(/^* /, '') }
def rtrim = { it.replaceAll(/ *$/, '') }
 
assert ltrim(' Groovy') == 'Groovy'
assert rtrim('Groovy ') == 'Groovy'
Or the same with Runtime Metaprogramming (it looks more consistent with trim()):
String.metaClass.ltrim = { return delegate.replaceAll(/^* /, '') }
String.metaClass.rtrim = { return delegate.replaceAll(/ *$/, '') }

assert ' Groovy'.ltrim() == 'Groovy'
assert 'Groovy '.rtrim() == 'Groovy'
assert '      Groovy'.ltrim() == 'Groovy'
assert 'Groovy       '.rtrim() == 'Groovy'
Test if a string contains only lower or uppercase letters:
assert 'Groovy'.matches(/.*[a-z].*/) == true
assert 'Groovy'.matches(/.*[A-Z].*/) == true
assert 'GROOVY'.matches(/.*[a-z].*/) == false
assert 'groovy'.matches(/.*[A-Z].*/) == false
Case insensitive contains:
assert 'A sEnTeNcE'.toLowerCase().contains('t') == true
assert 'A sEnTeNcE'.toUpperCase().contains('C') == true
assert ['A', 'B', 'C']*.toLowerCase().contains('b') == true
assert ['a', 'b', 'c']*.toUpperCase().contains('B') == true
Split a string by Uppercase letters using a Character Class Intersection pattern:
assert 'GroovyBaby'.split("(?=\\p{Upper})").join(" ") == 'Groovy Baby'
assert 'GroovyBaby'.split("(?=\\p{Lower})").join(" ") == 'G r o o v yB a b y'
assert 'Best.Language.Ever'.split("(?=\\p{Punct})").join(" ") == 'Best .Language .Ever'
assert 'Best,Language!Ever'.split("(?=\\p{Punct})").join(" ") == 'Best ,Language !Ever'
assert 'Best.Language.Ever'.split("(?=\\p{Punct})").join(" ").toString().replaceAll("\\.", "") == 'Best Language Ever' //escape the fullstop
Replace part of a string based on a pattern match:
assert '05.00'.replaceAll(/^0/, '') == '5.00'
Capitalisation:
s = "A silly string with words"
Groovy's capitalise only works on the first word of a sentence:
assert s.capitalize() == 'A silly string with words'
To get capitalisation of every word in a sentence, use this:
groovy.grape.Grape.grab(group:'org.apache.commons', module:'commons-lang3', version:'3.3.2')
assert org.apache.commons.lang3.text.WordUtils.capitalizeFully(s) == 'A Silly String With Words'

A side note about @Grab which you will see again further down: This annotation automatcally uses Ivy / Maven to get the jars you require,
and their dependencies. It can be a bit of a dark art to set up, but once it works, it works well. In theory, on a new install of Groovy, without a proxy,
this *should* work out of the box. I have it here for reference to the libs that are required, more than as a working example. YMMV.


If you want to get a substring of a string:
assert s[0..9] == 'A silly st'
You will get an error if the substring is larger than string. To prevent this, use take(size):
assert s.take(10) == 'A silly st'
assert s.take(1) == 'A'
assert s.take(100) == 'A silly string with words'
Here we have a list, but it is derived from a string:
assert 'I love Groovy'.tokenize() == ['I', 'love', 'Groovy']

Closures, ranges and other constructs

Closure:
def squareIt = { int x ->
    square = x**2
    return square
}

assert squareIt(5) == 25
Closure with uncertain amount of arguments (varargs):
def addUp = { int... varargs ->
    added = varargs.sum()
    return added
}

assert addUp(10, 11, 12) == 33
Ranges with closures, similar to a block in Ruby:
1.upto(4) { println "Number ${it}" }

3.times { println "Hello World ${it}" }

'Groovy'.collect { println 'letter:' + it  }

(1..10).collect { println it * 2 }

(1..10).each { println ((it % 5 == 0) ? "${it} is a factor of 5" : "${it} is not a factor of 5") }

('a'..'f').eachWithIndex { it, index ->
    println "${it} is at position ${index + 1}"
}
sum() works a little differently:
def range = (1..1000) as Integer[]
assert range.sum() == 500500
No continue in closures:
def list = (1..10).collect()
In loops you can continue...
for (i in list) {
    if (i < 6) {
        continue
    }
    println i
}
...in closures you can't continue, but you can use return...
list.each {
    if (it < 6) {
	return
    }
    println it
}
...return also works in the catch in a try-catch.
list.each {
    try {
        def undef = 1.0 / 0
    }
    catch(Exception e) {
        println "${e} caught for ${it}, but looping will continue"
        return
    }
}
Elvis operator in a closure:
def urkel() {
    if ('a'.equals('a')) { 
        return 'a'
    }
    else {
	return 'b' 
    }
}
//or
def elvis = ('a'.equals('a') ? 'a' : 'b')

assert urkel() == 'a'
assert elvis == 'a'
One line FizzbBuzz with the Elvis operator:
(1..100).each { println ( (it % 3  ? "" : "Fizz") + ( it % 5 ? "" : "Buzz") ?: it ) }
I had a case where I had to convert a decimal datatype to an integer of the total value of the size before and after the decimal point:
[ 's', '1000', '12.2', '15', 'A', '1', 'D', 'A.b', '12.', '.2'].each {
    if (it.contains(".") && it =~ /\d.*\.\d.*/) { // if it has a decimal and integers on either side
    	println it.tokenize(".").sum { it.toInteger() } //add the two digits
    }
}
Round numbers up to factors of four:
[3, 4, 15, 16, 151694653, 1022].each { upToFour(it) }

def upToFour(num) {
    while (num % 4 != 0) {
    	num++
    }
    println num
}
Return multiple values from a method:
def returnTwo() {
    def a = 1
    def b = 2
    return [a, b]
}

assert returnTwo() == [1, 2]
println returnTwo().getClass() //or
assert returnTwo().class == java.util.ArrayList //or
assert returnTwo() instanceof java.util.ArrayList // we'll use this going forward
Enum and switch in one example:
enum WeekdayColours {
    Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
	
    public static String getColour(WeekdayColours day) {
    	String colour = null
    	switch(day) {
            case Monday: colour = 'GREEN'; break
            case Tuesday: colour = 'YELLOW'; break
            case Wednesday: colour = 'GREY'; break
            case Thursday: colour = 'BLUE'; break
            case Friday: colour = 'BROWN'; break
            case Saturday: colour = 'BLACK'; break
            case Sunday: colour = 'WHITE'; break
            default: println "Something else"
	}
	return colour
    }
}
  
a = WeekdayColours.Friday
assert WeekdayColours.getColour(a) == 'BROWN'

Collection iteration

Lists
[1, 2, 3, 4, 5, 6, 7, 8, 9].each { println it * 2 }

[1, 2, 3, 4, 5, 6, 7, 8, 9].each { i ->
    if ( i % 2 == 0 ) {
        println i * 2
    } 
}
li = [14, 12, 35, 22, -5, 46, 98, 53, 29, 87, 44]
assert li instanceof java.util.ArrayList

assert li[0] == 14
assert li[-1] == 44
assert li.getAt(1) == 12
assert li.get(1) == 12
assert li.getAt(-1) == 44
assert li.count(12) == 1
assert li.intersect([20, 21, 22, 23, 24, 25, 26, 27, 28, 29]) == [22, 29]
Change a value:
li.set(1, 88)
assert li.getAt(1) == 88
li.set(1, 12)
assert li.get(1) == 12

assert li.size() == 11
assert li.min() == -5
assert li.max() == 98
assert li.sum() == 435
Let's make our own average method using Runtime Metaprogramming:
List.metaClass.avg = {
    return delegate.size() ? delegate.sum() / delegate.size() : 0
}
assert li.avg() == 39.5454545455
Cool. Continuing with lists:
assert li.join("|") == '14|12|35|22|-5|46|98|53|29|87|44'

assert li.find { it > 97 } == 98
assert li.find { it.toString() =~ /^4.*/ } == 46
assert li.findAll { it.toString() =~ /^4.*/ } == [46, 44]
assert (li.contains(35)) ? 'yes' : 'no' == 'no'

assert li.findAll { it < 15 } == [14, 12, -5]
assert li.findIndexOf{ it == 12 } == 1
assert li.contains(13) == false
assert li.isEmpty() == false

li2 = li
li2.addAll([7, 32])
assert li2 == [14, 12, 35, 22, -5, 46, 98, 53, 29, 87, 44, 7, 32]
li2.removeElement(-5)
assert li2.sort() == [7, 12, 14, 22, 29, 32, 35, 44, 46, 53, 87, 98]
assert li2.reverse() == [98, 87, 53, 46, 44, 35, 32, 29, 22, 14, 12, 7]
Sorting list elements:
li3 = ['i', 'l', 'o', 'v', 'e', 'g', 'r', 'o', 'o', 'v', 'y']
assert li3.each  { it } == ['i', 'l', 'o', 'v', 'e', 'g', 'r', 'o', 'o', 'v', 'y']
assert li3.toSorted().each  { it } == ['e', 'g', 'i', 'l', 'o', 'o', 'o', 'r', 'v', 'v', 'y']
assert li3.toUnique().each  { it } == ['i', 'l', 'o', 'v', 'e', 'g', 'r', 'y']
assert li3.toSorted().toUnique().each  { it } == ['e', 'g', 'i', 'l', 'o', 'r', 'v', 'y']
Sort is case sensitive:
li4 = ['I', 'l', 'o', 'V', 'e', 'G', 'r', 'o', 'O', 'v', 'y']
assert li4.each  { it } == ['I', 'l', 'o', 'V', 'e', 'G', 'r', 'o', 'O', 'v', 'y']
assert li4.toSorted().each  { it } == ['G', 'I', 'O', 'V', 'e', 'l', 'o', 'o', 'r', 'v', 'y']
assert li4.toUnique().each  { it } == ['I', 'l', 'o', 'V', 'e', 'G', 'r', 'O', 'v', 'y']
assert li4.toSorted().toUnique().each  { it } == ['G', 'I', 'O', 'V', 'e', 'l', 'o', 'r', 'v', 'y']
For non-case sensitive sort use this:
assert li4.sort{it.toLowerCase()} == ['e', 'G', 'I', 'l', 'o', 'o', 'O', 'r', 'V', 'v', 'y']
assert li4.sort{it.toUpperCase()}.toUnique() == ['e', 'G', 'I', 'l', 'o', 'O', 'r', 'V', 'v', 'y']
assert li4.sort{it.toUpperCase()} == ['e', 'G', 'I', 'l', 'o', 'o', 'O', 'r', 'V', 'v', 'y']
assert li4.sort{it.toUpperCase()}.toUnique() == ['e', 'G', 'I', 'l', 'o', 'O', 'r', 'V', 'v', 'y']
Regex and capturing Match object:
li5 = ['Blinky', 'Pinky', 'Inky', 'Clyde']

li5.each { i ->			        //use if statement else you'll get an error if there's no match
   def m = i =~ /^(C.*)/ 		//brackets around the match object you want to capture
   if (m) { assert m[0][1] == 'Clyde' } //first capture in match
}

li5.each { i ->
    def m = i =~ /(.*e)$/
    if (m) { assert m[0][1][3] == 'd' } //fourth element of capture
}
Overlaps:
li6 = ['a', 'a', 'b', 'c', 'd', 'e', 'e']
li7 = ['a', 'b', 'c', 'd', 'e', 'f']
def overlaps = li6.intersect(li7)
assert overlaps == ['a', 'b', 'c', 'd', 'e']
Combines:
def combo = li6.plus(li7)
assert combo == ['a', 'a', 'b', 'c', 'd', 'e', 'e', 'a', 'b', 'c', 'd', 'e', 'f']
Difference (not literal, just elements):
def diff = li7.minus(li6) //removes all occurences of element !
assert diff == ['f']
Literal difference (one directional):
def literalDiff = []
li6.each { if (li6.count(it) > 1) { literalDiff.add(it) } }
assert literalDiff == ['a', 'a', 'e', 'e']
Duplicates:
def duplicates = li6.findAll { a -> li6.findAll { b -> b == a }.size() > 1 }.unique()
assert duplicates == ['a', 'e']

Filter:
def (passed, failed) = [39, 56, 67, 76, 82, 89, 91].split{ it > 60 }
// or
println ([39, 56, 67, 76, 82, 89, 91].groupBy { it > 60 ? true : false })

Maps
m = [sky:'blue', grass:'green', ocean:'blue', ground:'brown', sun:'yellow']

assert m['grass'] == 'green'
assert m.get('ocean') == 'blue'
assert m.size() == 5

m.put('night', 'black')
assert m['night'] == 'black'
assert m.size() == 6

m.remove('sun')
assert m.size() == 5

m.each { key, value -> if (key.startsWith("gr")) println value }

def found = m.find{ it.key == "sky" }?.value
assert found.equals("blue")
assert found instanceof java.lang.String

def allFound = m.findAll{ key, value -> key.startsWith('gr') }
assert allFound == ([grass:'green', ground:'brown'])
assert allFound instanceof java.util.HashMap

def allFoundRegex = m.findAll{ key, value -> key.matches(/^gr.*/) }
assert allFoundRegex == ([grass:'green', ground:'brown'])
Reverse sort a map:
map = ['02.00':2, '06.00':6, '01.00':1, '04.00':4, '05.00':5, '03.00':3]
println map.sort { -it.value }
Accessing map keys and values:
inv = ['telephone':'Bell', 'aeroplane':'Wright', 'lightbulb':'Edison',\
       'penicillin':'Pasteur', 'pi':'Archimedes', 'electricity':'Franklin', 'telegraph':'Morse']
Find value by key:
assert inv['pi'] == 'Archimedes'
assert inv.get('pi') == 'Archimedes'
Find key by value (you would think it is straightforward):
assert inv.find { it.value == 'Wright' }?.key == 'aeroplane'
Test if map contains key or value:
assert inv.containsKey('pi') == true
assert inv.containsValue('Archimedes') == true

Random numbers and strings

The ability to create random numbers or strings is often handy for quickly generating test data. Here we define four methods that provide four different types of random data.
def randStr() { //random size
    return new Random().with { (1..(Math.abs(new Random().nextInt() % 5) + 1))
			    .collect { (('A'..'Z')+('0'..'9')+('a'..'z'))
			    .join()[ nextInt( (('A'..'Z')+('0'..'9')+('a'..'z'))
			    .join().length() ) ] }
			    .join() }
}
	
def randNum() { //random size
    return Math.abs(new Random().nextInt() % (Math.abs(new Random().nextInt()))) + 1
}

def randStr(desiredSize) { 
    return new Random().with { (1..desiredSize)
        		    .collect { (('A'..'Z')+('0'..'9')+('a'..'z'))
			    .join()[ nextInt( (('A'..'Z')+('0'..'9')+('a'..'z'))
			    .join().length() ) ] }
			    .join() }
}
	
def randNum(maxSize) {
    return Math.abs(new Random().nextInt() % maxSize) + 1 
}

println "random string: ${randStr()}"
println "random number: ${randNum()}"
println "random string of 10 characters: ${randStr(10)}"
println "random number between 1 and 3: ${randNum(3)}"
println "random number between 1 and a million: ${randNum(1000000)}"

Filesystem interaction

Files are easy to work with:
def myFile = new File("./src/myfile.txt")
myFile.write("Hello from Groovy\n")  //overwrites existing file
myFile << "More text\n" // appends
myFile.append("Last line")

assert myFile.text instanceof java.lang.String
println myFile.text
println myFile.text.size()

assert myFile.readLines() instanceof java.util.ArrayList
println myFile.readLines().size()
Get current file details:
File fileObject = new File("${this.class.getName()}.groovy")
println fileObject.name
println fileObject.absolutePath
Use date and time in a filename:
import java.text.SimpleDateFormat
testDate = new SimpleDateFormat('yyyy-MM-dd HH-mm').format(new Date())
def logFile = new File("./src/log-${testDate}.txt")
A note on comparing and formatting dates - you can reverse-engineer dates in other formats:
Date date1 = Date.parse( 'dd-MMM-yyyy', '07-OCT-2018' )
newDate1 = date1.format( 'd-M-yyyy' ) 
assert newDate1 == '7-10-2018'

Date date2 = Date.parse( 'dd MMM yy', '7 Oct 18' )
newDate2 = date2.format( 'd-M-yyyy' )
assert newDate2 == '7-10-2018'

Date date3 = Date.parse( 'dd/MM/yy', '7/10/18' )
newDate3 = date3.format( 'd-M-yyyy' )
assert newDate3 == '7-10-2018'
Get last modified date and time of files:

new File('.').eachFileRecurse { file ->
    println "On ${new Date(file.lastModified()).format('EEE MMM dd hh:mm:ss a yyyy')} ${file.name} was modified"
}
Recursive search:
import static groovy.io.FileType.FILES
List li = []
new File('.').eachFileRecurse(FILES) { f -> 
    if(f.name.endsWith('.groovy')) {
	println "file name ${f} found" 
	f.readLines().each { l ->
	    if (l.startsWith("import")) {
		println "found import: ${l}"
		li.add(l)
	    }
	}
    }
}

li.sort().unique().each { i ->
    println i
}
Extremely fast file sort:
new File("./src/sorted.txt").write(new File("./src/unsorted.txt").with { it.text = it.readLines().findAll { it }.sort().unique().join('\n') })
File lines as string:
new File("./src/siteSnippetsV5.groovy").eachLine { line ->
    assert line instanceof java.lang.String
    assert line.length() instanceof java.lang.Integer
}
File lines as list:
List listFileContents = new File('./src/siteSnippetsV5.groovy').collect { it }
assert listFileContents instanceof java.util.ArrayList
println listFileContents.size()
Find the longest line in a file:
index = 0
def elementLength = listFileContents[0].length()

listFileContents.eachWithIndex { element, index ->
    if (listFileContents[index].length() > elementLength) {
    	elementLength = listFileContents[index].length()
    }
}
println "${listFileContents[index]} is the longest line in the file"
AntBuilder - handy for manipulating files and directories:
groovy.grape.Grape.grab(group:'org.apache.ant', module:'ant', version:'1.9.8')
groovy.grape.Grape.grab(group:'org.apache.ant', module:'ant-launcher', version:'1.9.8')

def ant = new AntBuilder()

def file = new File(ant.project.baseDir,"src/myfile.txt")
assert file.exists()

ant.replace(file: file, token: ", );", value: ");")

ant.mkdir(dir: "../backup")

ant.copy( todir:"../backup" ) {
    fileset( dir:"./src/" )
  }
  
def zipfile = '../backup/backup.zip'
def current = './src'
ant.zip(destfile: zipfile) {
    fileset(dir: current) {
        include(name: '**/*.*')
    }
}

ant.delete(dir:'../backup',failonerror:false)
Reading CSV files:
groovy.grape.Grape.grab(group:'com.opencsv', module:'opencsv', version:'4.5')
groovy.grape.Grape.grab(group:'com.xlson.groovycsv', module:'groovycsv', version:'1.3')

import static com.xlson.groovycsv.CsvParser.parseCsv

for(line in parseCsv(new FileReader('./src/data.csv'), separator: ',')) {
	   println "$line.FirstName $line.LastName"
}

Handling xml

The simplicity of Groovy's standard library for XML handling is poetic.

HTML's handling of xml - not so poetic, if the display of this XML breaks in your browser, I'm sorry... here it is in a file

def myXmlString = '''
<transaction> <payment> <txID>68246894</txID> <customerName>Huey</customerName> <accountNo type="Current">15778047</accountNo> <txAmount>899</txAmount> </payment> <receipt> <txID>68246895</txID> <customerName>Dewey</customerName> <accountNo type="Current">16288</accountNo> <txAmount>120</txAmount> </receipt> <payment> <txID>68246896</txID> <customerName>Louie</customerName> <accountNo type="Savings">89257067</accountNo> <txAmount>210</txAmount> </payment> <payment> <txID>68246897</txID> <customerName>Dewey</customerName> <accountNo type="Cheque">123321</accountNo> <txAmount>500</txAmount> </payment> </transaction>
'''
Note how the XML handle and gpath in your code is in line with the XML structure and xpath. The root node becomes your variable for the XML string.
def transaction = new XmlSlurper().parseText(myXmlString)
Get some values from nodes and elements:
assert transaction.payment.txAmount[0] == '899'
assert transaction.payment.accountNo.@type[1] == 'Savings'
assert transaction.payment[2].accountNo.@type == 'Cheque'
assert transaction.receipt[0].txID == '68246895'
Find values based on criteria:
transaction.payment.findAll { tx ->
    tx.txAmount.toInteger() > 300
}.each { tx ->
    println "${tx.customerName} made payment of ${tx.txAmount} with transaction id ${tx.txID}."
}
Search with closure and a note on traversing:
def accNos = { node -> node.text() }
def validAccTypes = { node -> node.@type in ['Current', 'Cheque'] }

//descends to lowest node
println transaction.depthFirst().findAll(validAccTypes).collect(accNos) //equivalent of:
println transaction.'**'.findAll(validAccTypes).collect(accNos)

//only iterates first generation child nodes
println transaction.children().findAll(validAccTypes).collect(accNos) //equivalent of:
println transaction.'*'.findAll(validAccTypes).collect(accNos)

//descends to lowest node, one parent node at a time
println transaction.breadthFirst().findAll(validAccTypes).collect(accNos)
Update a node or element's value in the XML:
transaction.payment[1].customerName = 'Bob'
def stringWriter = new StringWriter()
println groovy.xml.XmlUtil.serialize(transaction)
Replace a node:
transaction.receipt[0].replaceNode{
    receipt(receiptNo:"65413"){
    	   receiptdate("2018-09-30")
    	   customerName("Dewey")
	   accountType("Current")
	   accountNo("16288")
	   txAmount("120") }
}
println groovy.xml.XmlUtil.serialize( transaction )
Add a node:
def newTransaction = new XmlParser( false, true ).parseText( myXmlString )
newValue = "        <receiptDate>2018-09-30</receiptDate>"
newNode = new XmlParser( false, true ).parseText( newValue )
newTransaction.'**'.find { it.name() == 'receipt' }.children().add( 2, newNode )
println groovy.xml.XmlUtil.serialize( newTransaction )
Remove a node:
newTransaction.NameValuePairs.NameValuePair.findAll { it.name.text() == 'accountNo' }*.replaceNode{}
println groovy.xml.XmlUtil.serialize( newTransaction )
Print every node in the xml's gpath and it's value (courtesy of tim_yates):
import groovy.util.XmlSlurper
import groovy.util.slurpersupport.GPathResult

def transactions = new XmlSlurper().parseText(myXmlString)

def leaves = transactions.depthFirst().findAll { it.children().size() == 0 }

def path(GPathResult node) {
    def result = [node.name()]
    def pathWalker = [hasNext: { -> node.parent() != node }, next: { -> node = node.parent() }] as Iterator
    (result + pathWalker.collect { it.name() }).reverse().join('/')
}

leaves.each { node ->
    println "${path(node)} = ${node.text()}"
}

Database work

Working with a database in groovy is so straightforward, I am not even going to say much about it:
groovy.grape.Grape.grab(group:'mysql', module:'mysql-connector-java', version:'5.1.32')

import groovy.sql.Sql
def myDB = Sql.newInstance('jdbc:mysql://127.0.0.7:3306/employees', 'user1', 'pass1', 'com.mysql.jdbc.Driver')
Selecting one row:
def numberOfRecords = myDB.firstRow("select count(*) as count from employees.employees;")
println "There are ${numberOfRecords.count} in the employees table"
Rowset as iterable list:
myDB.eachRow("select first_name, last_name from employees.employees;"){
    println "${it.first_name} ${it.last_name}"
}
Changing data:
myDB.execute("update employees.employees
              set last_name = 'Bradford'
              where last_lame = 'Simmel'
              and first_name = 'Bezalel';")

myDB.executeInsert("INSERT INTO `employees` VALUES 
                    (10000,'1960-10-01','Peter','Morgan','M','1995-02-10');")
Remember to tidy up:
myDB.close()
Make a method that inserts for you:

The ? parameters is for JDBC to optimise the query by precompiling it with parameters. Always use it.
If you use "${}" gstrings in your query for variables, the SQL is not optimised by JDBC, and it will carp about it.

def insertEmployee(emp_no, birth_date, first_name, last_name, gender, hire_date) {
    def db = Sql.newInstance('jdbc:mysql://127.0.0.7:3306/employees', 
                           'user1', 'pass1', 'com.mysql.jdbc.Driver')
	
    db.executeInsert("insert into employees VALUES (?, ?, ?, ?, ?, ?);",
                      [emp_no, birth_date, first_name, last_name, gender, hire_date])
    db.close()
}
Connect to MS-SQL Server with domain authentication:
Sql.newInstance("jdbc:jtds:sqlserver://SERVERNAME:1433/dbname;
                 useLOBs=false;
                 instance=SERVERNAME;
                 autoCommit=true;
                 useNTLMv2=true;
                 domain=MYDOMAIN",
                 "MyUser", "MyPassword", "net.sourceforge.jtds.jdbc.Driver")

Graphing with charts

For long I looked for a library to use for making graphs. I finally found one that works well with Groovy, and that doesn't require writing lots of lines of code.

Bar chart:
groovy.grape.Grape.grab(group:'org.knowm.xchart', module:'xchart-parent', version:'3.5.2', type:'pom')
groovy.grape.Grape.grab(group:'org.knowm.xchart', module:'xchart', version:'3.5.2')

import org.knowm.xchart.CategoryChart
import org.knowm.xchart.CategoryChartBuilder
import org.knowm.xchart.SwingWrapper
import org.knowm.xchart.style.Styler.LegendPosition
import org.knowm.xchart.BitmapEncoder
import org.knowm.xchart.BitmapEncoder.BitmapFormat

CategoryChart chartB = new CategoryChartBuilder().width(800)
                                                .height(600)
                                                .title("Score Histogram")
                                                .xAxisTitle("Score")
                                                .yAxisTitle("Number")
                                                .build()

chartB.getStyler().setLegendPosition(LegendPosition.InsideNW)
chartB.getStyler().setHasAnnotations(true)

chartB.addSeries("test 1", [ 0, 1, 2, 3, 4 ], [ 4, 5, 9, 6, 5 ])

new SwingWrapper(chartB).displayChart()
BitmapEncoder.saveBitmap(chart, "./src/Bar.png", BitmapFormat.PNG)
Pie chart:
groovy.grape.Grape.grab(group:'org.knowm.xchart', module:'xchart-parent', version:'3.5.2', type:'pom')
groovy.grape.Grape.grab(group:'org.knowm.xchart', module:'xchart', version:'3.5.2')

import java.awt.Color
import org.knowm.xchart.PieChart
import org.knowm.xchart.PieChartBuilder
import org.knowm.xchart.SwingWrapper
import org.knowm.xchart.internal.chartpart.Chart
import org.knowm.xchart.BitmapEncoder
import org.knowm.xchart.BitmapEncoder.BitmapFormat

PieChart chartP = new PieChartBuilder().width(800)
                                      .height(600)
                                      .title(getClass().getSimpleName())
                                      .build()

Color[]  sliceColors = [ new Color(224, 68, 14),
                         new Color(230, 105, 62),
                         new Color(236, 143, 110),
                         new Color(243, 180, 159),
                         new Color(246, 199, 182) ]

chartP.getStyler().setSeriesColors(sliceColors)

chartP.addSeries("Gold", 24)
chartP.addSeries("Silver", 21)
chartP.addSeries("Platinum", 39)
chartP.addSeries("Copper", 17)
chartP.addSeries("Zinc", 40)
   
new SwingWrapper(chartP).displayChart()
BitmapEncoder.saveBitmap(chartP, "./src/Pie.png", BitmapFormat.PNG)
Line chart:
groovy.grape.Grape.grab(group:'org.knowm.xchart', module:'xchart-parent', version:'3.5.2', type:'pom')
groovy.grape.Grape.grab(group:'org.knowm.xchart', module:'xchart', version:'3.5.2')

import org.knowm.xchart.XYChart
import org.knowm.xchart.XYChartBuilder
import org.knowm.xchart.SwingWrapper
import org.knowm.xchart.style.Styler.LegendPosition
import org.knowm.xchart.style.Styler.TextAlignment
import org.knowm.xchart.internal.chartpart.Chart
import org.knowm.xchart.BitmapEncoder
import org.knowm.xchart.BitmapEncoder.BitmapFormat

List xData = new ArrayList()
List yData = new ArrayList()
for (int i = -3; i <= 3; i++) {
    xData.add(i)
    yData.add(Math.pow(10, i))
}

XYChart chartL = new XYChartBuilder().width(800)
                                    .height(600)
                                    .title("Powers of Ten")
                                    .xAxisTitle("Power")
                                    .yAxisTitle("Value")
                                    .build()
 
chartL.getStyler().setChartTitleVisible(true)
chartL.getStyler().setLegendPosition(LegendPosition.InsideNW)
chartL.getStyler().setYAxisLogarithmic(true)
chartL.getStyler().setXAxisLabelRotation(45)
 
chartL.getStyler().setXAxisLabelAlignment(TextAlignment.Right)
chartL.getStyler().setXAxisLabelRotation(90)
chartL.getStyler().setXAxisLabelRotation(0)

chartL.addSeries('10^x', xData, yData)

new SwingWrapper(chartL).displayChart()
BitmapEncoder.saveBitmap(chartL, "./src/Line.png", BitmapFormat.PNG)
Make a wordcloud with a different graphing library:
groovy.grape.Grape.grab(group:'com.kennycason', module:'kumo', version:'1.17', type:'pom')
groovy.grape.Grape.grab(group:'com.kennycason', module:'kumo-core', version:'1.17')
groovy.grape.Grape.grab(group:'com.kennycason', module:'kumo-api', version:'1.17')

import static groovy.io.FileType.FILES
import com.kennycason.kumo.CollisionMode
import com.kennycason.kumo.WordFrequency
import com.kennycason.kumo.WordCloud
import com.kennycason.kumo.bg.RectangleBackground
import com.kennycason.kumo.font.scale.LinearFontScalar
import com.kennycason.kumo.nlp.FrequencyAnalyzer
import java.awt.Dimension
import java.awt.Color

ofile = new File("./src/words.txt")
ofile.write('')

["./src"].each { dir ->
    findWords(dir, ofile)
 }

def findWords(dir, ofile) {
    new File(dir).eachFileRecurse(FILES) {
	if(it.name.endsWith('.groovy')) {
            //println it.name
            it.readLines().each { l ->
                if (! l.contains("//")) {
                    w = l.split()
                    w.each() { word ->
			if (! [ "//", "\\", '=', '==', '{', '}', '[', ']', '->',
                                '|', '>=', '<=', '>', '<', '+', '-'].contains(word)) {
                            //println word
			    ofile.append("${word}\n")
                        }
                    }
		}
	    }
	}
    }
}

FrequencyAnalyzer frequencyAnalyzer = new FrequencyAnalyzer()
List wordFrequencies = frequencyAnalyzer.load("./src/words.txt")
Dimension dimension = new Dimension(600, 600)
WordCloud wordCloud = new WordCloud(dimension, CollisionMode.RECTANGLE)
wordCloud.setPadding(0)
wordCloud.setBackground(new RectangleBackground(dimension))
wordCloud.setFontScalar(new LinearFontScalar(10, 40))
wordCloud.build(wordFrequencies)
wordCloud.writeToFile("./src/wordcloud.png")

Bonus level

I once needed to execute Python code from within Groovy. Don't ask. I used Jython.
groovy.grape.Grape.grab(group:'org.python', module:'jython-standalone', version:'2.7.1b3')

import org.python.util.PythonInterpreter
import org.python.core.*

class AJythonCall {
    static void callJython() throws PyException {
	
	PythonInterpreter jython = new PythonInterpreter()

	println "Hello, Jython"
	jython.exec("import sys")
	jython.exec("print sys")

	jython.set("a", new PyInteger(42))
	jython.exec("print a")
		
	jython.exec("x = 2 + 2")
	PyObject x = jython.get("x")

	println "x: " + x
	println "Bye, Jython"
    }
}

//or

class AJythonScriptCall {
    static void callJythonScript() throws PyException {
		
	PythonInterpreter jython = new PythonInterpreter()
		
	jython.exec("""
		    |num = 1
		    |if num >= 0:
		    |    if num == 0:
		    |        print("Zero")
		    |    else:
		    |        print("Positive number")
		    |else:
		    |    print("Negative number")
		    """.stripMargin())
	}
}

a = new AJythonCall().callJython()
b = new AJythonScriptCall().callJythonScript()

© 2001-2019 ou-ryperd.net