Groovy for testers Apache Groovy

Groovy for testers

I have used Groovy since late 2015. Now in the stable of the Apache Foundation, Groovy is such a pleasure to code in. For me it is like a happy intersection of Ruby, Python an Java. I have used all three those, and other languages extensively in test automation, but 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).

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.

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 and a page about VBScript for testers.

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.

String manipulation
Collection iteration
Random numbers and strings
Filesystem interaction
Handling xml
Database work
Graphing with charts
Bonus level (run Python code from Groovy)


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: " +["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'
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
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'
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']

Collection iteration

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
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) {
	println num
Return multiple values from a method:
def returnTwo() {
	def a = 1
	def b = 2
	return [a, b]

assert returnTwo() == [1, 2]
assert returnTwo() instanceof java.util.ArrayList
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'

li = [14, 12, 35, 22, -5, 46, 98, 53, 29, 87, 44]
println li.getClass()

assert li[0] == 14
assert li[-1] == 44
assert li.getAt(1) == 12
assert li.get(1) == 12
assert li.getAt(-1) == 44
Change a value:
assert li.getAt(1) == 88
assert li.get(1) == 14

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]
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', 'r', '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][2] == 'y' } //third element of capture

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 })

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

assert m.get('ocean') == 'blue'
assert m.size() == 5

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

println m[]
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")
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")

println myFile.text.getClass()
println myFile.text
println myFile.text.size()

println myFile.readLines().getClass()
println myFile.readLines().size()
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:
Date date = Date.parse( 'dd-MMM-yyyy', '07-OCT-2018' )
newDate = date.format( 'd-M-yyyy' ) 
assert newDate == '7-10-2018'
Get last modified date and time of files:

new File('.').eachFileRecurse { file ->
	println new Date(file.lastModified()).format('EEE MMM dd hh:mm:ss a yyyy')
Recursive search:
import static
List li = []
new File('.').eachFileRecurse(FILES) { f -> 
    if('.groovy')) {
	println "file name ${f} found" 
	f.readLines().each { l ->
	    if (l.startsWith("import")) {
		println "found import: ${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/'
def current = './src' zipfile) {
    fileset(dir: current) {
        include(name: '**/*.*')


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}."
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:
		   txAmount("120") }
println groovy.xml.XmlUtil.serialize( transaction )
Add a node:
def newTransaction = new XmlParser( false, true ).parseText( myXmlString )
newNode = "		<receiptDate>2018-09-30</receiptDate>"
fragmentToAdd = new XmlParser( false, true ).parseText( newNode )
newTransaction.'**'.find { == 'receipt' }.children().add( 2, fragmentToAdd )
println groovy.xml.XmlUtil.serialize( newTransaction )
Remove a node:
newTransaction.NameValuePairs.NameValuePair.findAll { == 'accountNo' }*.replaceNode{}
println groovy.xml.XmlUtil.serialize( newTransaction )

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://', '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 
Remember to tidy up:
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://', 
                            'user1', 'pass1', 'com.mysql.jdbc.Driver')
	db.executeInsert("insert into employees VALUES (?, ?, ?, ?, ?, ?);",
                            [emp_no, birth_date, first_name, last_name, gender, hire_date])
Connect to MS-SQL Server with domain authentication:
                    "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 is small in lines of code you have to write.

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.BitmapEncoder
import org.knowm.xchart.BitmapEncoder.BitmapFormat

CategoryChart chart = new CategoryChartBuilder().width(800)
                                                .title("Score Histogram")


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

new SwingWrapper(chart).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.internal.chartpart.Chart
import org.knowm.xchart.BitmapEncoder
import org.knowm.xchart.BitmapEncoder.BitmapFormat

PieChart chart1 = new PieChartBuilder().width(800)

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) ]


chart1.addSeries("Gold", 24)
chart1.addSeries("Silver", 21)
chart1.addSeries("Platinum", 39)
chart1.addSeries("Copper", 17)
chart1.addSeries("Zinc", 40)
new SwingWrapper(chart1).displayChart()
BitmapEncoder.saveBitmap(chart1, "./src/Pie.png", BitmapFormat.PNG)
Make a wordcloud with a different 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 com.kennycason.kumo.CollisionMode
import com.kennycason.kumo.WordFrequency
import com.kennycason.kumo.WordCloud
import com.kennycason.kumo.font.scale.LinearFontScalar
import com.kennycason.kumo.nlp.FrequencyAnalyzer
import java.awt.Dimension

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

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

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

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.setBackground(new RectangleBackground(dimension))
wordCloud.setFontScalar(new LinearFontScalar(10, 40))

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"

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

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

© 2003-2019