Contenu connexe
Similaire à Groovy Tutorial (20)
Groovy Tutorial
- 1. © ASERT 2006-2010
Groovy Tutorial
Scripting on the JVM
Dr Paul King
paulk@asert.com.au
@paulk_asert
ASERT, Australia
- 2. Topics
Introduction
• Simple Data Types
• Collective Data Types
• Control Structures
• Closures and Builders
© ASERT 2006-2010
• OO
• GDK methods
• XML
• Database interaction
• Testing
• Metaprogramming
ESDC 2010 - 2
- 3. What is Groovy?
• “Groovy is like a super version
of Java. It can leverage Java's
enterprise capabilities but also
has cool productivity features like closures,
DSL support, builders and dynamic typing.”
© ASERT 2006-2010
Groovy = Java – boiler plate code
+ mostly dynamic typing
+ closures
+ domain specific languages
+ builders
+ metaprogramming
+ GDK library
ESDC 2010 - 3
- 4. Groovy Goodies Overview
• Fully object oriented
• Closures: reusable
and assignable
pieces of code
• Operators can be • GPath: efficient
overloaded
© ASERT 2006-2010
object navigation
• Multimethods • GroovyBeans
• Literal declaration for • grep and switch
lists (arrays), maps,
ranges and regular • Templates, builder,
expressions swing, Ant, markup,
XML, SQL, XML-RPC,
Scriptom, Grails,
tests, Mocks ESDC 2010 - 4
- 5. Growing Acceptance …
A slow and steady start but now gaining in
momentum, maturity and mindshare
Now free
- 7. … Growing Acceptance …
© ASERT 2006-2010
Groovy and Grails downloads:
70-90K per month and growing ESDC 2010 - 7
- 8. … Growing Acceptance …
© ASERT 2006-2010
Source: http://www.micropoll.com/akira/mpresult/501697-116746
Source: http://www.grailspodcast.com/
ESDC 2010 - 8
- 9. … Growing Acceptance …
© ASERT 2006-2010
http://www.jroller.com/scolebourne/entry/devoxx_2008_whiteboard_votes
http://www.java.net
ESDC 2010 - 9
- 10. … Growing Acceptance …
What alternative JVM language are you using or intending to use
© ASERT 2006-2010
http://www.leonardoborges.com/writings
ESDC 2010 - 10
- 11. … Growing Acceptance …
© ASERT 2006-2010
http://it-republik.de/jaxenter/quickvote/results/1/poll/44 (translated using http://babelfish.yahoo.com)
ESDC 2010 - 11
- 12. … Growing Acceptance …
© ASERT 2006-2010
http://pollpigeon.com/jsf-grails-wicket/r/25665/
ESDC 2010 - 12
- 14. The Landscape of JVM Languages
mostly
dynamic
typing
© ASERT 2006-2010
Dynamic features call
for dynamic types Java bytecode calls
for static types
The terms “Java Virtual Machine” and “JVM” mean a Virtual Machine for the Java™ platform.
ESDC 2010 - 14
- 15. Groovy Starter
System.out.println("Hello, World!"); // optional semicolon,
println 'Hello, World!' // System.out, brackets,
// main() method, class defn
def name = 'Guillaume' // dynamic typing
println "$name, I'll get the car." // GString
String longer = """${name}, the car
is in the next row.""" // multi-line string
© ASERT 2006-2010
// with static typing
assert 0.5 == 1/2 // BigDecimal equals()
def printSize(obj) { // optional duck typing
print obj?.size() // safe dereferencing
}
def pets = ['ant', 'bee', 'cat'] // native list syntax
pets.each { pet -> // closure support
assert pet < 'dog' // overloading '<' on String
} // or: for (pet in pets)...
ESDC 2010 - 15
- 16. A Better Java...
import java.util.List;
import java.util.ArrayList;
class Erase {
private List removeLongerThan(List strings, int length) { This code
List result = new ArrayList();
for (int i = 0; i < strings.size(); i++) {
is valid
String s = (String) strings.get(i); Java and
if (s.length() <= length) {
result.add(s); valid Groovy
}
}
© ASERT 2006-2010
return result;
}
public static void main(String[] args) {
List names = new ArrayList();
names.add("Ted"); names.add("Fred");
names.add("Jed"); names.add("Ned");
System.out.println(names);
Based on an
Erase e = new Erase(); example by
List shortNames = e.removeLongerThan(names, 3); Jim Weirich
System.out.println(shortNames.size());
for (int i = 0; i < shortNames.size(); i++) { & Ted Leung
String s = (String) shortNames.get(i);
System.out.println(s);
}
}
}
ESDC 2010 - 16
- 17. ...A Better Java...
import java.util.List;
import java.util.ArrayList;
class Erase {
private List removeLongerThan(List strings, int length) { Do the
List result = new ArrayList();
for (int i = 0; i < strings.size(); i++) {
semicolons
String s = (String) strings.get(i); add anything?
if (s.length() <= length) {
result.add(s); And shouldn‟t
}
} we us more
© ASERT 2006-2010
}
return result; modern list
public static void main(String[] args) { notation?
List names = new ArrayList();
names.add("Ted"); names.add("Fred"); Why not
names.add("Jed"); names.add("Ned");
System.out.println(names);
import common
Erase e = new Erase(); libraries?
List shortNames = e.removeLongerThan(names, 3);
System.out.println(shortNames.size());
for (int i = 0; i < shortNames.size(); i++) {
String s = (String) shortNames.get(i);
System.out.println(s);
}
}
}
ESDC 2010 - 17
- 18. ...A Better Java...
class Erase {
private List removeLongerThan(List strings, int length) {
List result = new ArrayList()
for (String s in strings) {
if (s.length() <= length) {
result.add(s)
}
}
return result
}
public static void main(String[] args) {
© ASERT 2006-2010
List names = new ArrayList()
names.add("Ted"); names.add("Fred")
names.add("Jed"); names.add("Ned")
System.out.println(names)
Erase e = new Erase()
List shortNames = e.removeLongerThan(names, 3)
System.out.println(shortNames.size())
for (String s in shortNames) {
System.out.println(s)
}
}
}
ESDC 2010 - 18
- 19. ...A Better Java...
class Erase {
private List removeLongerThan(List strings, int length) {
List result = new ArrayList()
for (String s in strings) {
if (s.length() <= length) {
result.add(s)
Do we need
} the static types?
}
return result Must we always
}
have a main
public static void main(String[] args) { method and
© ASERT 2006-2010
List names = new ArrayList()
names.add("Ted"); names.add("Fred") class definition?
names.add("Jed"); names.add("Ned")
System.out.println(names) How about
Erase e = new Erase()
List shortNames = e.removeLongerThan(names, 3)
improved
System.out.println(shortNames.size()) consistency?
for (String s in shortNames) {
System.out.println(s)
}
}
}
ESDC 2010 - 19
- 20. ...A Better Java...
def removeLongerThan(strings, length) {
def result = new ArrayList()
for (s in strings) {
if (s.size() <= length) {
result.add(s)
}
}
return result
}
© ASERT 2006-2010
names = new ArrayList()
names.add("Ted")
names.add("Fred")
names.add("Jed")
names.add("Ned")
System.out.println(names)
shortNames = removeLongerThan(names, 3)
System.out.println(shortNames.size())
for (s in shortNames) {
System.out.println(s)
}
ESDC 2010 - 20
- 21. ...A Better Java...
def removeLongerThan(strings, length) {
def result = new ArrayList()
for (s in strings) {
if (s.size() <= length) {
result.add(s) Shouldn‟t we
}
} have special
return result notation for lists?
}
And special
© ASERT 2006-2010
names = new ArrayList() facilities for
names.add("Ted")
names.add("Fred") list processing?
names.add("Jed") Is „return‟
names.add("Ned")
System.out.println(names) needed at end?
shortNames = removeLongerThan(names, 3)
System.out.println(shortNames.size())
for (s in shortNames) {
System.out.println(s)
}
ESDC 2010 - 21
- 22. ...A Better Java...
def removeLongerThan(strings, length) {
strings.findAll{ it.size() <= length }
}
names = ["Ted", "Fred", "Jed", "Ned"]
System.out.println(names)
shortNames = removeLongerThan(names, 3)
System.out.println(shortNames.size())
shortNames.each{ System.out.println(s) }
© ASERT 2006-2010
ESDC 2010 - 22
- 23. ...A Better Java...
def removeLongerThan(strings, length) {
strings.findAll{ it.size() <= length }
}
Is the method
names = ["Ted", "Fred", "Jed", "Ned"] now needed?
System.out.println(names)
shortNames = removeLongerThan(names, 3) Easier ways to
System.out.println(shortNames.size()) use common
shortNames.each{ System.out.println(s) }
methods?
© ASERT 2006-2010
Are brackets
required here?
ESDC 2010 - 23
- 24. ...A Better Java...
names = ["Ted", "Fred", "Jed", "Ned"]
println names
shortNames = names.findAll{ it.size() <= 3 }
println shortNames.size()
shortNames.each{ println it }
© ASERT 2006-2010
ESDC 2010 - 24
- 25. ...A Better Java
names = ["Ted", "Fred", "Jed", "Ned"]
println names
shortNames = names.findAll{ it.size() <= 3 }
println shortNames.size()
shortNames.each{ println it }
© ASERT 2006-2010
[Ted, Fred, Jed, Ned]
3
Ted
Jed
Ned
ESDC 2010 - 25
- 26. Better JavaBeans...
import java.math.BigDecimal; ...
public class BikeJavaBean { public void setModel(String model) {
private String manufacturer; this.model = model;
private String model; }
private int frame;
private String serialNo; public int getFrame() {
private double weight; return frame;
private String status; }
private BigDecimal cost;
public String getSerialNo() {
public BikeJavaBean(String manufacturer, String model, return serialNo;
int frame, String serialNo, }
double weight, String status) {
this.manufacturer = manufacturer; public void setSerialNo(String serialNo) {
this.model = model; this.serialNo = serialNo;
this.frame = frame; }
this.serialNo = serialNo;
© ASERT 2006-2010
this.weight = weight; public double getWeight() {
this.status = status; return weight;
} }
public String toString() { public void setWeight(double weight) {
return "Bike:“ + this.weight = weight;
“n manufacturer -- " + manufacturer + }
"n model -- " + model +
"n frame -- " + frame + public String getStatus() {
"n serialNo -- " + serialNo + return status;
"n"; }
}
public void setStatus(String status) {
public String getManufacturer() { this.status = status;
return manufacturer; }
}
public BigDecimal getCost() {
public void setManufacturer(String manufacturer) { return cost;
this.manufacturer = manufacturer; }
}
public void setCost(BigDecimal cost) {
public String getModel() { this.cost = cost.setScale(3, BigDecimal.ROUND_HALF_UP);
return model; }
} }
...
Modified example from: http://www.ibm.com/developerworks/library/j-pg09196.html ESDC 2010 - 26
- 27. ...Better JavaBeans...
import java.math.BigDecimal; ...
public class BikeJavaBean { public void setModel(String model) {
Auto
private String manufacturer; this.model = model;
private String model;
private int frame;
} getters?
private String serialNo;
private double weight;
private String status;
public int getFrame() {
}
return frame; Auto
private BigDecimal cost;
public String getSerialNo() {
setters?
public BikeJavaBean(String manufacturer, String model, return serialNo;
int frame, String serialNo,
double weight, String status) {
} Auto
this.manufacturer = manufacturer;
this.model = model;
this.frame = frame; }
this.serialNo = serialNo; construction?
public void setSerialNo(String serialNo) {
this.serialNo = serialNo;
© ASERT 2006-2010
this.weight = weight; public double getWeight() {
this.status = status; return weight;
} }
public String toString() { public void setWeight(double weight) {
return "Bike:“ + this.weight = weight;
“n manufacturer -- " + manufacturer + }
"n model -- " + model +
"n frame -- " + frame + public String getStatus() {
"n serialNo -- " + serialNo + return status;
"n"; }
}
public void setStatus(String status) {
public String getManufacturer() { this.status = status;
return manufacturer; }
}
public BigDecimal getCost() {
public void setManufacturer(String manufacturer) { return cost;
this.manufacturer = manufacturer; }
}
public void setCost(BigDecimal cost) {
public String getModel() { this.cost = cost.setScale(3, BigDecimal.ROUND_HALF_UP);
return model; }
} }
...
ESDC 2010 - 27
- 28. ... Better JavaBeans ...
class BikeGroovyBean {
String manufacturer, model, serialNo, status
final Integer frame
Double weight
BigDecimal cost
public void setCost(BigDecimal newCost) {
cost = newCost.setScale(3, BigDecimal.ROUND_HALF_UP)
© ASERT 2006-2010
}
public String toString() {
return """Bike:
manufacturer -- $manufacturer
model -- $model More complicated than
frame -- $frame normal because we want:
serialNo -- $serialNo * Read only frame
""" * Custom setter for cost
} * Custom toString()
}
ESDC 2010 - 28
- 29. ... Better JavaBeans
class BikeGroovyBean {
String manufacturer, model, serialNo, status
Integer frame
Double weight
BigDecimal cost
}
© ASERT 2006-2010
In more typical
cases it is often
as simple as this.
ESDC 2010 - 29
- 30. Topics
• Introduction
Simple Data Types
Numbers, Other 'Primitives', Strings, Regexs, Dates & Times
• Collective Data Types
• Control Structures
•
© ASERT 2006-2010
Closures and Builders
• OO
• GDK methods
• XML
• Database interaction
• Testing
• Metaprogramming
ESDC 2010 - 30
- 31. Numbers
• Java numbers byte b1 = 42; Byte b2 = 42
short s1 = 20000; Short s2 = 20000
– But with primitive int i1 = 1000000000
types autoboxed to Integer i2 = 1000000000
def i3 = 42
equivalent wrapper long l1 = 1000000000000000000
types Long l2 = 1000000000000000000
def l3 = 42L
– Sizes supported:
© ASERT 2006-2010
Byte, Short, float f1 = 3.5f; Float f2 = 3.5
Integer, Long, def f3 = 3.5f
double d1 = 3.5e40; Double d2 = 3.5e40
Float, Double, def d3 = 3.5d
BigInteger, BigInteger bi = 30g
BigDecimal BigDecimal bd1 = 3.5g
def bd2 = 3.5
– Some special
treatment when assert 2 instanceof Integer
assert !2.equals(4)
handling arrays assert 3.5 instanceof BigDecimal
and in Interfaces assert (7/2) instanceof BigDecimal
ESDC 2010 - 31
- 32. Parsing Strings to Numbers
def string = "300"
assert string.isNumber() // check if string appears to be a number
// OK, now convert to number in different ways
def i4 = string.toInteger()
def i5 = string as int
def i6 = string as Integer
def i7 = new Integer(string)
def i8 = Integer.parseInt(string)
def i9 = (int) java.text.NumberFormat.instance.parse(string) // Long
© ASERT 2006-2010
string = "300.5"
def n1 = java.text.NumberFormat.instance.parse(string) // Double
def n2 = string.toFloat()
def n3 = string.toDouble()
def n4 = string.toBigDecimal()
integerPattern = /^[+-]?d+$/ // regex notation
assert '-36' ==~ integerPattern // explained later
assert !('abc' =~ integerPattern)
decimalPattern = /^-?(?:d+(?:.d*)?|.d+)$/
assert '37.5' ==~ decimalPattern
ESDC 2010 - 32
- 33. Other 'primitive' types
• boolean and char
– Again replaced by wrapper counterparts
automatically
– We'll see a little bit more about chars under Strings
– There is a notion called 'Groovy Truth' whereby any
expression can be used where Java would require a
© ASERT 2006-2010
boolean and the expression will be evaluated
• The numbers 0 and 0.0 and null objects return false
• Empty Strings, Lists, Maps, Matchers, ... return false
• Other objects return true
Found 4 vowels:
def vowels = 'aeiou' [e, i, o, u]
def sentences = ['The quick brown fox', 'Flyby'] Nothing found
sentences.each { sentence ->
def found = sentence.toList().intersect(vowels.toList())
if (found) println "Found ${found.size()} vowels: $found"
else println 'Nothing found'
} ESDC 2010 - 33
- 34. Strings...
• Several forms // normal strings
def firstname = 'Kate'
– Single quotes for assert firstname instanceof String
simple strings def surname = "Bush"
assert firstname * 2 == 'KateKate'
– Double quotes for def fullname =
GStrings which firstname + ' ' + surname
assert fullname == 'Kate Bush'
support variable
© ASERT 2006-2010
expansion // GString
fullname = "$firstname $surname"
– Slashy strings assert fullname instanceof GString
behave like assert fullname == 'Kate Bush'
GStrings but assert fullname - firstname == ' Bush'
assert fullname.padLeft(10) ==
preserve ' Kate Bush'
backslashes (great
for regex and // indexing (including ranges)
assert fullname[0..3] == firstname
directory names) assert fullname[-4..-1] == surname
– Multi-line versions assert fullname[5, 3..1] == 'Beta'
ESDC 2010 - 34
- 35. …Strings…
// Multi-line strings
def tt = ''' def lines = tt.split('n')
She sells, sea shells assert lines.size() == 2
By the sea shore''' lines = address.readLines()
assert lines.size() == 3
assert tt instanceof String assert lines[-1] == 'New York'
assert tt.startsWith('She')
assert tt.endsWith('shore') // slashy string: (almost) no escaping
assert tt.contains('n') def path = /C:WindowsSystem32/
© ASERT 2006-2010
def address = """ def plain = 'nrtbf$'
$fullname assert plain.size() == 7
123 First Ave def slashy = /nrtbf$/
New York assert slashy.size() == 14
""".trim()
// late binding trick with closures
println """ fullname = "${-> firstname} $surname"
--------------------- assert fullname == 'Kate Bush'
| $fullname | firstname = 'George'
| 123 First Ave | surname = 'Clooney'
| New York | assert fullname == 'George Bush'
--------------------- """
ESDC 2010 - 35
- 36. …Strings...
// more substrings (minus removes first occurrence)
string = 'hippopotamus'
assert string - 'hippo' - 'mus' + 'to' == 'potato'
assert string.replace('ppopotam','bisc') == 'hibiscus'
// processing characters (more on collections later)
def letters = 'apple'.toList()
assert letters == ['a', 'p', 'p', 'l', 'e']
© ASERT 2006-2010
assert letters[0] instanceof String
letters = 'apple'.toList()*.toCharacter()
def expected = ['a', 'p', 'p', 'l', 'e'] as char[]
assert letters == expected
assert letters[0] instanceof Character
assert letters instanceof List
assert expected instanceof char[]
string = "an apple a day"
assert string.toList().unique().sort().join() == ' adelnpy'
ESDC 2010 - 36
- 37. …Strings...
// reversing chars/words
assert 'string'.reverse() == 'gnirts'
assert 'two words'.split().reverse().join(' ') == 'words two'
string = 'Yoda said, "can you see this?"'
revwords = string.split(' ').reverse().join(' ')
assert revwords == 'this?" see you "can said, Yoda'
© ASERT 2006-2010
revwords = string.replaceAll(/(w*)/) {
all, match -> match.reverse() }
assert revwords == 'adoY dias, "nac uoy ees siht?"'
revwords = string.replaceAll(/(w*) (w*)(W*)/ * 3,
'$2 $1$3$8 $7$6$5 $4$9')
assert revwords == 'said Yoda, "this see you can?"'
• Find out more through Javadoc, GDK doc
– Can also lookup GString but usually no need
ESDC 2010 - 37
- 38. …Strings...
• javadoc
© ASERT 2006-2010
ESDC 2010 - 38
- 39. …Strings
• groovy-jdk (just the additions)
© ASERT 2006-2010
ESDC 2010 - 39
- 40. Regular Expressions...
assert "Hello World!" =~ /Hello/ // Find operator
assert "Hello World!" ==~ /Hellob.*/ // Match operator
def p = ~/Hellob.*/ // Pattern operator
assert p.class.name == 'java.util.regex.Pattern'
def input = "Voting is open between 01 Nov 2008 and 04 Nov 2008"
def dateFormat = /dd? ... d{4}/
def matches = (input =~ dateFormat)
© ASERT 2006-2010
assert matches[1] == '04 Nov 2008'
01 Nov 2008
matches.each { m -> println m } 04 Nov 2008
input.eachMatch(dateFormat) { m -> println m }
assert input.findAll(dateFormat) == ['01 Nov 2008', '04 Nov 2008']
println input.replaceAll(dateFormat, '?/?/?')
Voting is open between ?/?/? and ?/?/?
ESDC 2010 - 40
- 41. ...Regular Expressions
// "Voting is open between 01 Nov 2008 and 04 Nov 2008"
def dateFormatGroups = /(dd?) (...) (d{4})/
def matchingGroups = (input =~ dateFormatGroups)
assert matchingGroups[1][1] == '04'
matchingGroups.each { all, d, m, y -> println "$d/$m/$y" }
© ASERT 2006-2010
input.eachMatch(dateFormatGroups) { all, d, m, y ->
println "$d/$m/$y"
} 01/Nov/2008
04/Nov/2008
assert input.findAll(dateFormatGroups) {
full, d, m, y -> "$m $d of $y"
} == ['Nov 01 of 2008', 'Nov 04 of 2008']
println input.replaceAll(dateFormatGroups) {
full, d, m, y -> "$m $d ????"
} Voting is open between Nov 01 ???? and Nov 04 ????
ESDC 2010 - 41
- 42. Abbreviated regex notation
Symbol Meaning
. Any character
^ Start of line (or start of document, when in single-line mode)
$ End of line (or end of document, when in single-line mode)
d Digit character
D Any character except digits
s Whitespace character
S Any character except whitespace
w Word character
© ASERT 2006-2010
W Any character except word characters
b Word boundary
() Grouping
(x|y) x or y as in (Groovy|Java|Ruby)
x* Zero or more occurrences of x
x+ One or more occurrences of x
x? Zero or one occurrence of x
x{m,n} At least m and at most n occurrences of x
x{m} Exactly m occurrences of x
[a-f] Character class containing the characters a, b, c, d, e, f
[^a] Character class containing any character except a
(?=regex) Positive lookahead
(?<=text) Positive lookbehind
ESDC 2010 - 42
- 43. Dates...
• Similar to Java
– Use java.util.Date, java.util.Calendar, java.sql.Date, ...
– Can use static imports to help
– Or 3rd party package like Joda Time
• Special support for changing times:
© ASERT 2006-2010
date2 = date1 + 1 // day
date2 = date1 + 1.week – 3.days + 6.hours
• Utility methods for date input and output
– Date.parse(formatString, dateString)
– date.format(formatString)
– date.timeString, date.dateString, date.dateTimeString
• Potentially will change with JSR 310
ESDC 2010 - 43
- 44. ... Dates...
import static java.util.Calendar.getInstance as now
import java.util.GregorianCalendar as D
import org.codehaus.groovy.runtime.TimeCategory
println new D(2007,11,25).time
println now().time Tue Dec 25 00:00:00 EST 2007
Thu Jun 28 10:10:34 EST 2007
def date = new Date() + 1 Fri Jun 29 10:10:35 EST 2007
© ASERT 2006-2010
println date Tue Jul 17 11:10:35 EST 2007
Date was Jun/03/1998
use(TimeCategory) {
println new Date() + 1.hour + 3.weeks - 2.days
}
dateStr = "1998-06-03"
date = Date.parse("yyyy-MM-dd", dateStr)
println 'Date was ' + date.format("MMM/dd/yyyy")
def holidays = boxingDay..newYearsEve
ESDC 2010 - 44
- 45. ... Dates
import static java.util.Calendar.*
def nowCal = Calendar.instance
y = nowCal.get(YEAR)
Date nowDate = nowCal.time
def (m, d) = [nowDate[MONTH] + 1, nowDate[DATE]]
println "Today is $d $m $y"
nowCal.set DATE, 1
nowCal.set MONTH, FEBRUARY
println "Changed to $nowCal.time"
© ASERT 2006-2010
Today is 26 9 2008
Changed to Fri Feb 01 23:05:20 EST 2008
cal = Calendar.instance
cal.set 1988, APRIL, 4, 0, 0, 0
date = cal.time
def (doy, woy, y) = [DAY_OF_YEAR, WEEK_OF_YEAR,
YEAR].collect{ date[it] }
println "$date is day $doy and week $woy of year $y"
Mon Apr 04 00:00:00 EST 1988 is day 95 and week 15 of year 1988
ESDC 2010 - 45
- 49. Topics
• Introduction
• Simple Data Types
Collective Data Types
• Control Structures
• Closures and Builders
© ASERT 2006-2010
• OO
• GDK methods
• XML
• Database interaction
• Testing
• Metaprogramming
ESDC 2010 - 49
- 50. Better Lists, Maps, Ranges
• Lists/Collections
– Special literal syntax
– Additional common methods (operator overloading)
def list = [3, new Date(), 'Jan']
assert list + list == list * 2
• Maps
© ASERT 2006-2010
– Special literal syntax & GDK methods
def map = [a: 1, b: 2]
assert map['a'] == 1 && map.b == 2
• Ranges
– Special syntax for various kinds of ranges
def letters = 'a'..'z'
def numbers = 0..<10
assert letters.size() + numbers.size() == 36
ESDC 2010 - 50
- 51. Topics
• Introduction
• Simple Data Types
• Collective Data Types
Control Structures
• Closures and Builders
© ASERT 2006-2010
• OO
• GDK methods
• XML
• Database interaction
• Testing
• Metaprogramming
ESDC 2010 - 51
- 52. Better Control Structures: Branching/Loops
• Branching and loops • Loop-like Closures
for (int i = 0; i < n; i++) { } myMap.each { key, value ->
for (Type item : iterable) { } println "$key : $value"
for (item in iterable) { } }
// if (condition) ... // each, every, collect, any,
// else if (condition) ... // inject, find, findAll,
// else ... // upto, downto, times, grep,
© ASERT 2006-2010
// throw, try...catch...finally // reverseEach, eachMatch,
// for, while // eachWithIndex, eachLine
// d = a ? b : c // eachFile, eachFileMatch,
// a = b ?: c // eachByte, combinations ...
// d = a?.b?.c
• Groovy Truth words = ['bob', 'alpha', 'ehcache',
if (1) // ... 'rotator', 'omega', 'reviver']
if (object) // ... bigPalindromes = words.findAll{ w ->
if (collection) // ... w == w.reverse() && w.size() > 4}
if (map) // ... assert bigPalindromes ==
if (matcher) // ... ['ehcache', 'rotator', 'reviver']
ESDC 2010 - 52
- 53. Better Control Structures: Switch
switch (10) {
case 0 : assert false ; break
case 0..9 : assert false ; break
case [8,9,11] : assert false ; break
case Float : assert false ; break
case {it%3 == 0} : assert false ; break
case ~/../ : assert true ; break
© ASERT 2006-2010
default : assert false ; break
}
• Extensible
– Implement your own isCase() method
ESDC 2010 - 53
- 54. Topics
• Introduction
• Simple Data Types
• Collective Data Types
• Control Structures
Closures and Builders
© ASERT 2006-2010
• OO
• GDK methods
• XML
• Database interaction
• Testing
• Metaprogramming
ESDC 2010 - 54
- 55. Using Closures...
• Traditional mainstream languages
– Data can be stored in variables, passed around,
combined in structured ways to form more complex
data; code stays put where it is defined
• Languages supporting closures
– Data and code can be stored in variables, passed
© ASERT 2006-2010
around, combined in structured ways to form more
complex algorithms and data
doubleNum = { num -> num * 2 }
println doubleNum(3) // => 6
processThenPrint = { num, closure ->
num = closure(num); println "num is $num"
}
processThenPrint(3, doubleNum) // => num is 6
processThenPrint(10) { it / 2 } // => num is 5
ESDC 2010 - 55
- 56. ...Using Closures...
import static Math.* Algorithm piE differs by 1.0206946399193839E-11
Algorithm piF differs by 1.0070735356748628E-9
Algorithm piC differs by 2.668102068170697E-7
piA = { 22 / 7 } Algorithm piD differs by 4.813291008076703E-5
piB = { 333/106 } Algorithm piB differs by 8.321958979307098E-5
piC = { 355/113 } Algorithm piA differs by 0.001264489310206951
piD = { 0.6 * (3 + sqrt(5)) }
piE = { 22/17 + 37/47 + 88/83 }
piF = { sqrt(sqrt(2143/22)) }
© ASERT 2006-2010
howCloseToPI = { abs(it.value() - PI) }
algorithms = [piA:piA, piB:piB, piC:piC,
piD:piD, piE:piE, piF:piF]
findBestPI(algorithms)
def findBestPI(map) {
map.entrySet().sort(howCloseToPI).each { entry ->
def diff = howCloseToPI(entry)
println "Algorithm $entry.key differs by $diff"
}
}
ESDC 2010 - 56
- 57. ...Using Closures...
• Used for many things in Groovy:
• Iterators new File('/x.txt').eachLine {
println it
• Callbacks }
• Higher-order functions
• Specialized control structures
• Dynamic method definition
© ASERT 2006-2010
• Resource allocation 3.times { println 'Hi' }
• Threads
[0, 1, 2].each { number ->
• Continuations println number
}
def houston(Closure doit) {
(10..1).each { count -> [0, 1, 2].each { println it}
doit(count)
} def printit = { println it }
} [0, 1, 2].each printit
houston { println it }
ESDC 2010 - 57
- 58. ...Using Closures...
map = ['a': 1, 'b': 2]
map.each {key, value -> map[key] = value * 2}
assert map == ['a': 2, 'b': 4]
doubler = {key, value -> map[key] = value * 2}
map.each(doubler)
© ASERT 2006-2010
assert map == ['a': 4, 'b': 8]
def doubleMethod(entry) {
map[entry.key] = entry.value * 2
}
doubler = this.&doubleMethod
map.each(doubler)
assert map == ['a': 8, 'b': 16]
ESDC 2010 - 58
- 59. ...Using Closures...
assert [1, 2, 3].grep{ it < 3 } == [1, 2]
assert [1, 2, 3].any{ it % 2 == 0 }
assert [1, 2, 3].every{ it < 4 }
assert (1..9).collect{it}.join() == '123456789'
assert (1..4).collect{it * 2}.join() == '2468'
def add = { x, y -> x + y }
© ASERT 2006-2010
def mult = { x, y -> x * y }
assert add(1, 3) == 4
assert mult(1, 3) == 3
def min = { x, y -> [x, y].min() }
def max = { x, y -> [x, y].max() }
def triple = mult.curry(3); assert triple(2) == 6
def atLeastTen = max.curry(10)
assert atLeastTen(5) == 10
assert atLeastTen(15) == 15
ESDC 2010 - 59
- 60. ...Using Closures
def pairWise(list, Closure invoke) {
if (list.size() < 2) return []
def next = invoke(list[0], list[1])
return [next] + pairWise(list.tail(), invoke)
}
© ASERT 2006-2010
// using min, max, etc. From previous slide
assert pairWise(1..5, add) == [3, 5, 7, 9]
assert pairWise(1..5, mult) == [2, 6, 12, 20]
assert pairWise(1..5, min) == [1, 2, 3, 4]
assert pairWise(1..5, max) == [2, 3, 4, 5]
assert 'cbaxabc' == ['a', 'b', 'c'].inject('x') {
result, item -> item + result + item
}
ESDC 2010 - 60
- 61. Better Design Patterns: Builder
• Markup Builder
<html>
import groovy.xml.*
<head>
def page = new MarkupBuilder()
<title>Hello</title>
page.html {
</head>
head { title 'Hello' }
<body>
body {
© ASERT 2006-2010
<ul>
ul {
<li>world 1</li>
for (count in 1..5) {
<li>world 2</li>
li "world $count"
<li>world 3</li>
} } } }
<li>world 4</li>
<li>world 5</li>
</ul>
</body>
</html>
ESDC 2010 - 61
- 62. SwingBuilder
import java.awt.FlowLayout
builder = new groovy.swing.SwingBuilder()
langs = ["Groovy", "Ruby", "Python", "Pnuts"]
gui = builder.frame(size: [290, 100],
title: 'Swinging with Groovy!') {
panel(layout: new FlowLayout()) {
panel(layout: new FlowLayout()) {
for (lang in langs) {
© ASERT 2006-2010
checkBox(text: lang)
}
}
button(text: 'Groovy Button', actionPerformed: {
builder.optionPane(message: 'Indubitably Groovy!').
createDialog(null, 'Zen Message').show()
})
button(text: 'Groovy Quit',
actionPerformed: {System.exit(0)})
}
}
gui.show()
Source: http://www.ibm.com/developerworks/java/library/j-pg04125/
ESDC 2010 - 62
- 63. SwingXBuilder
import groovy.swing.SwingXBuilder
© ASERT 2006-2010
import static java.awt.Color.*
import static java.lang.Math.*
def swing = new SwingXBuilder()
def frame = swing.frame(size: [300, 300]) {
graph(plots: [
[GREEN, {value -> sin(value)}],
[BLUE, {value -> cos(value)}],
[RED, {value -> tan(value)}]
])
}.show()
ESDC 2010 - 63
- 64. GraphicsBuilder
def colors = ['red','darkOrange','blue','darkGreen']
(0..3).each { index ->
star( cx: 50 + (index*110), cy: 50, or: 40, ir: 15,
borderColor: 'black', count: 2+index, fill: colors[index] )
star( cx: 50 + (index*110), cy: 140, or: 40, ir: 15,
borderColor: 'black‘, count: 7+index, fill: colors[index] )
}
© ASERT 2006-2010
ESDC 2010 - 64
- 65. AntBuilder...
def ant = new AntBuilder()
ant.echo("hello") // let's just call one task
// create a block of Ant using the builder pattern
ant.sequential {
myDir = "target/AntTest/"
mkdir(dir: myDir)
© ASERT 2006-2010
copy(todir: myDir) {
fileset(dir: "src/test") {
include(name: "**/*.groovy")
}
}
echo("done")
}
// now let's do some normal Groovy again
file = new File("target/test/AntTest.groovy")
assert file.exists()
ESDC 2010 - 65
- 66. ...AntBuilder
def ant = new AntBuilder()
ant.echo(file:'Temp.java', '''
class Temp {
public static void main(String[] args) {
System.out.println("Hello");
}
}
© ASERT 2006-2010
''')
ant.with {
javac(srcdir:'.', includes:'Temp.java', fork:'true')
java(classpath:'.', classname:'Temp', fork:'true')
echo('Done')
}
// =>
// [javac] Compiling 1 source file
// [java] Hello
// [echo] Done
ESDC 2010 - 66
- 67. Using AntLibs: Maven Ant Tasks & AntUnit
import static groovy.xml.NamespaceBuilder.newInstance as namespace
def ant = new AntBuilder()
def mvn = namespace(ant, 'antlib:org.apache.maven.artifact.ant')
def antunit = namespace(ant, 'antlib:org.apache.ant.antunit')
direct = [groupId:'jfree', artifactId:'jfreechart', version:'1.0.9']
indirect = [groupId:'jfree', artifactId:'jcommon', version:'1.0.12']
// download artifacts
© ASERT 2006-2010
mvn.dependencies(filesetId:'artifacts') { dependency(direct) }
// print out what we downloaded
ant.fileScanner { fileset(refid:'artifacts') }.each { println it }
// use AntUnit to confirm expected files were downloaded
def prefix = System.properties.'user.home' + '/.m2/repository'
[direct, indirect].each { item ->
def (g, a, v) = [item.groupId, item.artifactId, item.version]
antunit.assertFileExists(file:"$prefix/$g/$a/$v/$a-${v}.jar")
}
C:Userspaulk.m2repositoryjfreejcommon1.0.12jcommon-1.0.12.jar
C:Userspaulk.m2repositoryjfreejfreechart1.0.9jfreechart-1.0.9.jar
ESDC 2010 - 67
- 68. Topics
• Introduction
• Simple Data Types
• Collective Data Types
• Control Structures
• Closures and Builders
© ASERT 2006-2010
OO
• GDK methods
• XML
• Database interaction
• Testing
• Metaprogramming
ESDC 2010 - 68
- 69. Object Orientation
• Similar capabilities to Java
– Define classes, interfaces, enums, annotations
• Differences to Java
– Classes (and interfaces etc.) public by default
– Methods public by default
–
© ASERT 2006-2010
Property support within classes (auto-setters/getters)
– Multimethods (runtime dispatch – "duck typing")
class BikeGroovyBean {
String manufacturer, model, serialNo, status
Integer frame
Double weight
BigDecimal cost
}
ESDC 2010 - 69
- 70. Object Orientation – Runtime Interfaces
– Java has these methods in java.util.Collections:
– In Groovy, no need to create a class implementing
Comparator having a compare method, just use a map
© ASERT 2006-2010
of Closures instead (though Groovy has special support
for this example which is even more concise):
def animals = ['Antelope', 'Bee', 'Zebra']
def comparator = [
compare:{ a, b -> a.size() <=> b.size() }
] as Comparator
assert comparator instanceof Comparator
assert Collections.min(animals, comparator) == 'Bee'
assert Collections.max(animals, comparator) == 'Antelope'
ESDC 2010 - 70
- 71. Topics
• Introduction
• Simple Data Types
• Collective Data Types
• Control Structures
• Closures and Builders
© ASERT 2006-2010
• OO
GDK methods
• XML
• Database interaction
• Testing
• Metaprogramming
ESDC 2010 - 71
- 72. Better File Manipulation...
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.io.File; // ...
import java.io.FileOutputStream; private static void processFile(File file, PrintWriter out) {
import java.io.PrintWriter; FileInputStream fis = null;
import java.io.FileNotFoundException; InputStreamReader isr = null;
import java.io.IOException; BufferedReader reader = null;
import java.io.BufferedReader; try {
import java.io.InputStreamReader; fis = new FileInputStream(file);
import java.io.FileInputStream; isr = new InputStreamReader(fis);
reader = new BufferedReader(isr);
public class PrintJavaSourceFileLinesThatContainTheWordJava { String nextline;
public static void main(String[] args) { int count = 0;
File outfile = new File("result.txt"); while ((nextline = reader.readLine()) != null) {
outfile.delete(); count++;
File basedir = new File(".."); if (nextline.toLowerCase().contains("java")) {
List<File> files = new ArrayList<File>(); out.println("File '" + file +
files.add(basedir); "' on line " + count);
© ASERT 2006-2010
FileOutputStream fos = null; out.println(nextline);
PrintWriter out = null; out.println();
try { }
fos = new FileOutputStream(outfile); }
out = new PrintWriter(fos); } catch (FileNotFoundException e) {
while (!files.isEmpty()) { e.printStackTrace();
File file = files.remove(0); } catch (IOException e) {
if (file.isDirectory()) { e.printStackTrace();
files.addAll(Arrays.asList(file.listFiles())); } finally {
} else { if (reader != null) {
if (file.getName().endsWith(".java")) { try { reader.close(); }
processFile(file, out); catch (IOException e) { e.printStackTrace(); }
} }
} if (isr != null) {
} try { isr.close(); }
} catch (FileNotFoundException e) { catch (IOException e) { e.printStackTrace(); }
e.printStackTrace(); }
} finally { if (fis != null) {
if (out != null) { out.close(); } try { fis.close(); }
if (fos != null) { catch (IOException e) { e.printStackTrace(); }
try { fos.close(); } }
catch (IOException e) { e.printStackTrace(); } }
} }
} }
}
// ...
ESDC 2010 - 72
- 73. ...Better File Manipulation...
import java.util.List;
import
import
java.util.ArrayList;
java.util.Arrays;
boilerplate
import java.io.File; // ...
import java.io.FileOutputStream; private static void processFile(File file, PrintWriter out) {
import java.io.PrintWriter; FileInputStream fis = null;
import java.io.FileNotFoundException; InputStreamReader isr = null;
import java.io.IOException; BufferedReader reader = null;
import java.io.BufferedReader; try {
import java.io.InputStreamReader; fis = new FileInputStream(file);
import java.io.FileInputStream; isr = new InputStreamReader(fis);
reader = new BufferedReader(isr);
public class PrintJavaSourceFileLinesThatContainTheWordJava { String nextline;
public static void main(String[] args) { int count = 0;
File outfile = new File("result.txt"); while ((nextline = reader.readLine()) != null) {
outfile.delete(); count++;
File basedir = new File(".."); if (nextline.toLowerCase().contains("java")) {
List<File> files = new ArrayList<File>(); out.println("File '" + file +
files.add(basedir); "' on line " + count);
© ASERT 2006-2010
FileOutputStream fos = null; out.println(nextline);
PrintWriter out = null; out.println();
try { }
fos = new FileOutputStream(outfile); }
out = new PrintWriter(fos); } catch (FileNotFoundException e) {
while (!files.isEmpty()) { e.printStackTrace();
File file = files.remove(0); } catch (IOException e) {
if (file.isDirectory()) { e.printStackTrace();
files.addAll(Arrays.asList(file.listFiles())); } finally {
} else { if (reader != null) {
if (file.getName().endsWith(".java")) { try { reader.close(); }
processFile(file, out); catch (IOException e) { e.printStackTrace(); }
} }
} if (isr != null) {
} try { isr.close(); }
} catch (FileNotFoundException e) { catch (IOException e) { e.printStackTrace(); }
e.printStackTrace(); }
} finally { if (fis != null) {
if (out != null) { out.close(); } try { fis.close(); }
if (fos != null) { catch (IOException e) { e.printStackTrace(); }
try { fos.close(); } }
catch (IOException e) { e.printStackTrace(); } }
} }
} }
}
// ...
ESDC 2010 - 73
- 74. ...Better File Manipulation
def out = new File('result.txt')
out.delete()
new File('..').eachFileRecurse {file ->
if (file.name.endsWith('.groovy')) {
file.eachLine { line, num ->
if (line.toLowerCase().contains('groovy'))
out <<
© ASERT 2006-2010
"File '$file' on line $numn$linenn"
}
}
} File '..filessrcPrintGroovySourceLines.groovy' on line 4
if (file.name.endsWith('.groovy')) {
File '..filessrcPrintGroovySourceLines.groovy' on line 6
if (line.toLowerCase().contains('groovy'))
File '..jdbcsrcJdbcGroovy.groovy' on line 1
import groovy.sql.Sql
…
ESDC 2010 - 74
- 75. Better Process Management...
// windows version (minimal error/output)
def p = "cmd /c dir *.xml".execute()
p.waitFor()
if (p.exitValue()) println p.err.text
else println p.text
----- stdout -----
Volume in drive D is DATA
Volume Serial Number is FC5C-B39D
© ASERT 2006-2010
Directory of D:svntrunk-groovygroovy-core
// unix version
27/06/2009 10:13 AM 38,478 build.xml
def p = "ls *.xml".execute() 29/06/2009 06:53 PM 27,617 pom.xml
def err = new StringBuffer() 2 File(s) 66,095 bytes
0 Dir(s) 15,503,597,568 bytes free
def out = new StringBuffer()
// could ignore/use Stream below ----- stderr -----
Return Code: 1
p.consumeProcessOutput(out, err) ls: *.xml: No such file or directory
def exitValue = p.waitFor()
if (err) println """----- stderr -----
Return Code: $exitValue
$err"""
if (out) println "----- stdout -----n$out"
ESDC 2010 - 75
- 76. ...Better Process Management
sout: tstfl.grvy
serr:
def sout = new StringBuffer()
def serr = new StringBuffer()
proc1 = 'tr -d o'.execute()
proc2 = 'tr -d e'.execute()
© ASERT 2006-2010
proc3 = 'tr -d i'.execute()
proc3.consumeProcessOutput(sout, serr)
proc1 | proc2 | proc3
[proc1, proc2].each{ it.consumeProcessErrorStream(serr) }
proc1.withWriter { writer ->
writer << 'testfile.groovy'
}
proc3.waitForOrKill(1000)
println 'sout: ' + sout
println 'serr: ' + serr
ESDC 2010 - 76
- 77. Better Thread Management
Thread.start{ Hello from thread 2
4.times { Hello from thread 1
println 'Hello from thread 1' Hello from main thread
sleep 400 Hello from main thread
} Hello from thread 2
} Hello from thread 1
Thread.start{ Hello from main thread
© ASERT 2006-2010
5.times { Hello from thread 2
println 'Hello from thread 2' Hello from main thread
sleep 300 Hello from thread 1
} Hello from main thread
} Hello from thread 2
7.times { Hello from main thread
println 'Hello from main thread' Hello from thread 2
sleep 200 Hello from thread 1
} Hello from main thread
ESDC 2010 - 77
- 78. Topics
• Introduction
• Simple Data Types
• Collective Data Types
• Control Structures
• Closures and Builders
© ASERT 2006-2010
• OO
• GDK methods
XML
• Database interaction
• Testing
• Metaprogramming
ESDC 2010 - 78
- 79. Better XML Manipulation...
<records> records.xml
<car name='HSV Maloo' make='Holden' year='2006'>
<country>Australia</country>
<record type='speed'>Production Pickup Truck with speed of 271kph</record>
</car>
© ASERT 2006-2010
<car name='P50' make='Peel' year='1962'>
<country>Isle of Man</country>
<record type='size'>Smallest Street-Legal Car at 99cm wide and 59 kg weight</record>
</car>
<car name='Royale' make='Bugatti' year='1931'>
<country>France</country>
<record type='price'>Most Valuable Car at $15 million</record>
</car>
</records>
ESDC 2010 - 79
- 80. ...Better XML Manipulation...
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
public class FindYearsJava {
public static void main(String[] args) {
© ASERT 2006-2010
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document document = builder.parse(new File("records.xml"));
NodeList list = document.getElementsByTagName("car");
for (int i = 0; i < list.getLength(); i++) {
Node n = list.item(i);
Node year = n.getAttributes().getNamedItem("year");
System.out.println("year = " + year.getTextContent());
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ESDC 2010 - 80
- 81. ...Better XML Manipulation...
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node; boilerplate
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
public class FindYearsJava {
public static void main(String[] args) {
© ASERT 2006-2010
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document document = builder.parse(new File("records.xml"));
NodeList list = document.getElementsByTagName("car");
for (int i = 0; i < list.getLength(); i++) {
Node n = list.item(i);
Node year = n.getAttributes().getNamedItem("year");
System.out.println("year = " + year.getTextContent());
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ESDC 2010 - 81
- 82. ...Better XML Manipulation
def records = new XmlParser().parse("records.xml")
records.car.each {
© ASERT 2006-2010
println "year = ${it.@year}"
}
year = 2006
year = 1962
year = 1931
ESDC 2010 - 82
- 83. XML Processing Options
• Reading XML
– Special Groovy support: XmlParser, XmlSlurper, DOMCategory
• All support GPath, XmlSlurper features Lazy loading
– Or Groovy sugar for your current favorites: DOM, SAX, StAX,
DOM4J, JDom, XOM, XPath, XSLT, XQuery, etc.
• Creating XML
– Special Groovy support: MarkupBuilder and
© ASERT 2006-2010
StreamingMarkupBuilder
– Or again, enhanced syntax for your current favorites
• Updating XML
– Using above: read followed by create
– Can be done with XmlParser, XmlSlurper, DOMCategory
– Or with your Java favorites
• Verifying XML
– Also DTD, W3C XML Schema, Relax NG in a similar fashion to
Java mechanisms for these features
ESDC 2010 - 83
- 84. More Details: XmlSlurper
def records = new XmlSlurper().parse("records.xml")
assert 3 == records.car.size() // 3 records in total
assert 10 == records.depthFirst().collect{ it }.size() // 10 nested nodes
// test properties of the first record
def firstRecord = records.car[0]
assert 'car' == firstRecord.name()
assert 'Holden' == firstRecord.@make.toString() && 'Australia' == firstRecord.cou
// 2 cars have an 'e' in the make
assert 2 == records.car.findAll{ it.@make.toString().contains('e') }.size()
© ASERT 2006-2010
// 2 cars have an 'e' in the make
assert 2 == records.car.findAll{ it.@make =~ '.*e.*' }.size()
// makes of cars that have an 's' followed by an 'a' in the country
assert ['Holden', 'Peel'] == records.car.findAll{ it.country =~ '.*s.*a.*' }.@make.coll
// types of records
assert ['speed', 'size', 'price'] == records.depthFirst().grep{ it.@type != '' }.'@type'*
assert ['speed', 'size', 'price'] == records.'**'.grep{ it.@type != '' }.'@type'*.toString
// check parent() operator
def countryOne = records.car[1].country
assert 'Peel' == countryOne.parent().@make.toString() && 'Peel' == countryOne.'.
// names of cars with records sorted by year
def names = records.car.list().sort{ it.@year.toInteger() }.'@name'*.toString()
assert ['Royale', 'P50', 'HSV Maloo'] == names
ESDC 2010 - 84