├── .gitignore ├── README ├── chapters ├── closures │ ├── anonymous.groovy │ ├── arguments.groovy │ ├── asinterface.groovy │ ├── available.groovy │ ├── convertmethod.groovy │ ├── curry.groovy │ ├── delegate.groovy │ ├── docall.groovy │ ├── parameters.groovy │ └── passtomethod.groovy ├── collections │ ├── addmap.groovy │ ├── arrays.groovy │ ├── collect.groovy │ ├── combinations.groovy │ ├── complexkeys.groovy │ ├── count.groovy │ ├── find.groovy │ ├── grep.groovy │ ├── group.groovy │ ├── headtail.groovy │ ├── immutable.groovy │ ├── inject.groovy │ ├── intersect.groovy │ ├── join.groovy │ ├── listasstring.groovy │ ├── listtomap.groovy │ ├── loops.groovy │ ├── mapasinterface.groovy │ ├── mapdefaultvalues.groovy │ ├── orderby.groovy │ ├── permutations.groovy │ ├── ranges.groovy │ ├── reverse.groovy │ ├── sortmap.groovy │ ├── split.groovy │ ├── submap.groovy │ ├── subscript.groovy │ ├── subsequences.groovy │ ├── subtractmap.groovy │ ├── sum.groovy │ └── transpose.groovy ├── datetime │ ├── cleartime.groovy │ ├── common.groovy │ ├── converttimestamp.groovy │ ├── setvalues.groovy │ ├── subscript.groovy │ ├── timecategory.groovy │ └── updates.groovy ├── files │ ├── filenamefinder.groovy │ ├── filetypes.groovy │ ├── operators.groovy │ ├── rename.groovy │ ├── traversing.groovy │ └── withwriter.groovy ├── metaclass │ ├── addmethods.groovy │ ├── getproperty.groovy │ ├── metadsl.groovy │ ├── methodarguments.groovy │ ├── override.groovy │ └── staticmethod.groovy ├── numbers │ ├── absolute.groovy │ ├── integerdivision.groovy │ ├── power.groovy │ └── round.groovy ├── regexp │ ├── matcher.groovy │ └── pattern.groovy ├── strings │ ├── base64.groovy │ ├── capitalize.groovy │ ├── character.groovy │ ├── continuation.groovy │ ├── convertboolean.groovy │ ├── expand.groovy │ ├── gstring.groovy │ ├── isnumber.groovy │ ├── lines.groovy │ ├── multiply.groovy │ ├── normalize.groovy │ ├── padding.groovy │ ├── process.groovy │ ├── remove.groovy │ ├── replaceall.groovy │ ├── split.groovy │ ├── sprintf.groovy │ ├── stringtypes.groovy │ ├── stripleadingspaces.groovy │ ├── stripmargin.groovy │ ├── subscript.groovy │ ├── tokenize.groovy │ ├── translation.groovy │ └── url.groovy ├── syntax │ ├── antbuilder.groovy │ ├── askeyword.groovy │ ├── astype.groovy │ ├── classhierarchy.groovy │ ├── classinfo.groovy │ ├── configslurper.groovy │ ├── constructor.groovy │ ├── defaultargument.groovy │ ├── defaultimports.groovy │ ├── delegate.groovy │ ├── dynamicmethods.groovy │ ├── elvis.groovy │ ├── equality.groovy │ ├── exception.groovy │ ├── expando.groovy │ ├── gpath.groovy │ ├── grab.groovy │ ├── grabresolver.groovy │ ├── grape.groovy │ ├── groovybeans.groovy │ ├── immutable.groovy │ ├── importalias.groovy │ ├── indexedproperty.groovy │ ├── inheritconstructor.groovy │ ├── inoperator.groovy │ ├── inspect.groovy │ ├── keywordmethodname.groovy │ ├── lazy.groovy │ ├── multipleassignment.groovy │ ├── namedparameters.groovy │ ├── newify.groovy │ ├── newinstance.groovy │ ├── optionalreturn.groovy │ ├── paramdefault.groovy │ ├── parenthesis.groovy │ ├── powerassert.groovy │ ├── returnvaluecasting.groovy │ ├── safenavigation.groovy │ ├── semicolons.groovy │ ├── shutdownhook.groovy │ ├── singleton.groovy │ ├── spaceship.groovy │ ├── spreaddotoperator.groovy │ ├── spreadoperator.groovy │ ├── switchoperator.groovy │ ├── synchronizedannotation.groovy │ ├── templateengine.groovy │ ├── truth.groovy │ └── with.groovy └── xml │ ├── markupbuilder.groovy │ ├── streamingmarkupbuilder.groovy │ ├── typeconversion.groovy │ ├── xmlparser.groovy │ └── xmlslurper.groovy ├── checker └── checker.groovy └── template ├── A5 ├── A5 │ ├── 5.83x8.26_Back_EN.png │ ├── 5.83x8.26_Front_EN.png │ └── a5_template.doc └── ReadMe_EN.pdf ├── CrownQuarto ├── Crown Quarto │ ├── 7.44x9.68_Back_EN.png │ ├── 7.44x9.68_Front_EN.png │ ├── crown_template.doc │ └── sample.doc └── ReadMe_EN.pdf ├── Royal ├── ReadMe_EN.pdf └── Royal │ ├── 6.13x9.21_Back_EN.png │ ├── 6.13x9.21_Front_EN.png │ └── royal_template.doc └── USTrade-Paperback ├── ReadMe_EN.pdf └── US Trade - Paperback ├── 6x9_Back_EN.png ├── 6x9_Front_EN.png └── ustrade_template.doc /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | *.iml 3 | /chapters/closures/interfaces/ 4 | /chapters/files/filetypes/ 5 | /chapters/files/traverse-sample/ 6 | /checker/blog.html 7 | /checker/file1 8 | /checker/file1.doc 9 | /checker/file1.txt 10 | /checker/file2 11 | /checker/file2.doc 12 | /checker/file2.txt 13 | /checker/file3 14 | /checker/file3.doc 15 | /checker/file3.txt 16 | /checker/file4.doc 17 | /checker/file4.txt 18 | /checker/file5.doc 19 | /checker/file5.txt 20 | /checker/filetypes/ 21 | /checker/interfaces/ 22 | /checker/newname.groovy 23 | /checker/sample.txt 24 | /checker/test 25 | /checker/test.zip 26 | /checker/traverse-sample/ -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/README -------------------------------------------------------------------------------- /chapters/closures/anonymous.groovy: -------------------------------------------------------------------------------- 1 | def date = { 2 | def d = new Date() 3 | d.set year: 2010, month: Calendar.DECEMBER, date: 1 4 | d.format "dd-MM-yyyy" 5 | }() 6 | 7 | assert date == '01-12-2010' 8 | 9 | def text = { 10 | def result = '' 11 | "mrhaki".size().times { 12 | result += it 13 | } 14 | result 15 | }.call() 16 | 17 | assert text == '012345' 18 | -------------------------------------------------------------------------------- /chapters/closures/arguments.groovy: -------------------------------------------------------------------------------- 1 | // Closure with default single 'it' argument. 2 | def minusOne = { it - 1 } 3 | assert minusOne('Groovy String with 1.') == 'Groovy String with .' 4 | assert minusOne(42) == 41 5 | 6 | // Closure with single named argument. 7 | def namedArg = { value -> value * 2 } 8 | assert namedArg('Groovy ') == 'Groovy Groovy ' 9 | assert namedArg(42) == 84 10 | 11 | // Closure with multiple named arguments. 12 | def moreArgs = { a, b -> a + b } 13 | assert moreArgs('Groovy ', 'Java') == 'Groovy Java' 14 | assert moreArgs(42, 2) == 44 15 | 16 | // Closure without arguments, even no 'it'. 17 | def noArgs = { -> 'Groovy closure without arguments' } 18 | assert noArgs() == 'Groovy closure without arguments' 19 | -------------------------------------------------------------------------------- /chapters/closures/asinterface.groovy: -------------------------------------------------------------------------------- 1 | def baseDir = new File('interfaces') 2 | baseDir.mkdir() 3 | ('a'..'e').each { 4 | def file = new File(baseDir, "${it}.txt") 5 | file << 'sample' 6 | } 7 | 8 | def filter = { it.path ==~ /.*\.txt$/ } as FileFilter 9 | assert baseDir.listFiles(filter).size() == 5 10 | -------------------------------------------------------------------------------- /chapters/closures/available.groovy: -------------------------------------------------------------------------------- 1 | class Simple { 2 | String language 3 | 4 | def whatDoYouSpeak() { 5 | language 6 | } 7 | 8 | def say(String text) { 9 | "You say $text in $language" 10 | } 11 | } 12 | 13 | def s = new Simple(language: 'Groovy') 14 | 15 | assert s.metaClass.respondsTo(s, 'whatDoYouSpeak') 16 | assert Simple.metaClass.respondsTo(s, 'say') 17 | assert s.metaClass.respondsTo(s, 'say', String) 18 | assert !s.metaClass.respondsTo(s, 'say', Integer) // No say(Integer) method. 19 | assert Simple.metaClass.respondsTo(s, 'toString') // Method in parent object. 20 | 21 | assert s.metaClass.hasProperty(s, 'language') 22 | assert Simple.metaClass.respondsTo(s, 'getLanguage') // Get/set methods are generated. 23 | assert s.metaClass.respondsTo(s, 'setLanguage') 24 | -------------------------------------------------------------------------------- /chapters/closures/convertmethod.groovy: -------------------------------------------------------------------------------- 1 | // Simple list with names. 2 | def names = ['groovy', 'grails', 'mrhaki'] 3 | 4 | // Simple closure. 5 | names.each { println 'Normal closure says: Hello ' + it + '!' } 6 | 7 | // Groovy method to convert to closure. 8 | def groovySays(s) { 9 | "Groovy says: Hello ${s}!" 10 | } 11 | // Use .& syntax to convert method to closure. 12 | names.each this.&groovySays 13 | 14 | // Simple Java class to show we can turn a Java method 15 | // also into a Groovy closure. 16 | public class JavaObject { 17 | public static String javaSays(final String s) { 18 | return "Java says: Hello " + s + "!"; 19 | } 20 | } 21 | 22 | // Convert Java method to closure and use it. 23 | def javaSays = JavaObject.&javaSays 24 | names.each javaSays 25 | -------------------------------------------------------------------------------- /chapters/closures/curry.groovy: -------------------------------------------------------------------------------- 1 | //TODO// Simple curry sets value of first argument. 2 | def addNumbers = { x, y -> x + y } 3 | def addOne = addNumbers.curry(1) 4 | assert addOne(4) == 5 5 | 6 | // General closure to use a filter on a list. 7 | def filterList = { filter, list -> list.findAll(filter) } 8 | // Closure to find even numbers. 9 | def even = { it % 2 == 0 } 10 | // Closure to find odd numbers. 11 | def odd = { !even(it) } 12 | // Other closures can be curry parameters. 13 | def evenFilterList = filterList.curry(even) 14 | def oddFilterList = filterList.curry(odd) 15 | assert [0,2,4,6,8] == evenFilterList(0..8) 16 | assert [1,3,5,7] == oddFilterList(0..8) 17 | 18 | // Recipe to find text in lines. 19 | def findText = { filter, handler, text -> 20 | text.eachLine { 21 | filter(it) ? handler(it) : null 22 | } 23 | } 24 | // Recipe for a regular expression filter. 25 | def regexFilter = { pattern, line -> line =~ pattern } 26 | 27 | // Create filter for searching lines with "Groovy". 28 | def groovyFilter = regexFilter.curry(/Groovy/) 29 | // Create handler to print out line. 30 | def printHandler = { println "Found in line: $it" } 31 | 32 | // Create specific closure as clone of processText to 33 | // search with groovyFilter and print out found lines. 34 | def findGroovy = findText.curry(groovyFilter, printHandler) 35 | 36 | // Invoke the closure. 37 | findGroovy('''Groovy rules! 38 | And Java? 39 | Well... Groovy needs the JVM... 40 | ''') 41 | // This will output: 42 | // Found in line: Groovy rules! 43 | // Foudn in line: Well... Groovy needs the JVM... 44 | 45 | 46 | @Grab('commons-lang:commons-lang:2.5') 47 | import org.apache.commons.lang.RandomStringUtils as RSU 48 | 49 | def randomClosure = { size, letters, numbers -> 50 | // Invoke RandomStringUtils.random() method. 51 | RSU.random size, letters, numbers 52 | } 53 | 54 | def randomNumbers = randomClosure.rcurry(false, true) // letters = false, numbers = true 55 | def randomLetters = randomClosure.ncurry(1, true, false) // letters = true, numbers = false 56 | 57 | println randomClosure(10, true, true) // Sample output: VG7mffNAdA 58 | println randomNumbers(10) // Sample output: 8099670444 59 | println randomLetters(10) // Sample output: ZOHlHewEPU 60 | -------------------------------------------------------------------------------- /chapters/closures/delegate.groovy: -------------------------------------------------------------------------------- 1 | // Script variable and method. 2 | count = 0 3 | def info() { 4 | "Count value is $count." 5 | } 6 | // Closure to increment a count variable and invoke a info() method. 7 | def printInfo = { 8 | count++ 9 | info() 10 | } 11 | // Delegate is by default set to owner, so the script in this case. 12 | assert printInfo() == "Count value is 1." 13 | 14 | // Simple class to show closure delegate feature. 15 | class Post { 16 | int count 17 | def info() { "This is Groovy Goodness post #$count!" } 18 | } 19 | // Change closure resolver so first the delegate is used. 20 | printInfo.resolveStrategy = Closure.DELEGATE_FIRST 21 | // Set delegate to Post object. 22 | printInfo.delegate = new Post(count: 100) 23 | // Invoke closure again. 24 | assert printInfo() == "This is Groovy Goodness post #101!" 25 | -------------------------------------------------------------------------------- /chapters/closures/docall.groovy: -------------------------------------------------------------------------------- 1 | // Use doCall to invoke Closure self. 2 | def sizeList = { list, counter = 0 -> 3 | if (list.size() == 0) { 4 | counter 5 | } else { 6 | doCall(list.tail(), counter + 1) // Call closure self. 7 | } 8 | } 9 | 10 | assert sizeList([1,2,3,4,5]) == 5 11 | -------------------------------------------------------------------------------- /chapters/closures/parameters.groovy: -------------------------------------------------------------------------------- 1 | // Two simple closure with one and two parameters. 2 | def oneArg = { it.toUpperCase() } 3 | def twoArg = { String s, upper -> 4 | if (upper) { 5 | s.toUpperCase() 6 | } else { 7 | s.toLowerCase() 8 | } 9 | } 10 | 11 | // Closure with check for number of parameters and types. 12 | def runClosure(cl) { 13 | switch (cl.maximumNumberOfParameters) { 14 | case 1: 15 | assert cl.parameterTypes == [java.lang.Object] 16 | cl.call('Groovy') 17 | break 18 | case 2: 19 | assert cl.parameterTypes == [java.lang.String, java.lang.Object] 20 | cl('Groovy', false) 21 | break 22 | } 23 | } 24 | 25 | assert runClosure(oneArg) == 'GROOVY' 26 | assert runClosure(twoArg) == 'groovy' 27 | -------------------------------------------------------------------------------- /chapters/closures/passtomethod.groovy: -------------------------------------------------------------------------------- 1 | // Method with two arguments. Last argument is a closure. 2 | def work(input, cl) { 3 | cl(input) 4 | } 5 | // Define a closure. 6 | def assertJava = { it == 'Java' } 7 | 8 | // 'Normal' method invocation. 9 | work('Java', assertJava) 10 | // No parenthesis. 11 | work 'Java', assertJava 12 | // Anonymous closure as argument. 13 | work('Groovy', { 14 | assert it == 'Groovy' 15 | }) 16 | // Last argument is closure and can be outside parenthesis. 17 | work('Groovy') { 18 | assert it == 'Groovy' 19 | } 20 | // Opening bracket on new line. 21 | // If we want a code block (e.g. static initializer) 22 | // instead of closure we must use ; to separate code. 23 | work('Groovy') 24 | { 25 | assert it == 'Groovy' 26 | } 27 | // Pay attention, no parenthesis, so comma is needed again! 28 | work 'Groovy', { 29 | assert it == 'Groovy' 30 | } 31 | // Does not work: 32 | // 33 | // Comma between argument list needed: 34 | // work 'Groovy' { 35 | // assert it == 'Groovy' 36 | // } 37 | -------------------------------------------------------------------------------- /chapters/collections/addmap.groovy: -------------------------------------------------------------------------------- 1 | def m = [name: 'Java', max: 10, sort: 'name'] 2 | def other = [name: 'Groovy', direction: 'asc'] 3 | 4 | m << other 5 | assert m == [name: 'Groovy', max: 10, sort: 'name', direction: 'asc'] 6 | -------------------------------------------------------------------------------- /chapters/collections/arrays.groovy: -------------------------------------------------------------------------------- 1 | def stringArray = new String[3] 2 | assert stringArray instanceof String[] 3 | stringArray[0] = 'mrhaki' 4 | stringArray.putAt(1, 'Groovy') // New syntax. 5 | stringArray[2] = 'Java' 6 | 7 | assert stringArray.getAt(0) == 'mrhaki' // Just another way to get a value. 8 | assert stringArray[1] == 'Groovy' 9 | assert stringArray[-1] == 'Java' // Negative indeces allowed. 10 | assert stringArray[0..1] == ['mrhaki', 'Groovy'] // We can use ranges. 11 | assert stringArray[0, 2] == ['mrhaki', 'Java'] 12 | 13 | assert stringArray.length == 3 // Normal length property for arrays. 14 | assert stringArray.size() == 3 // Groovy adds size() method as well. 15 | 16 | // We can use min() and max() methods. 17 | assert [102,301,42,83].min() == 42 18 | assert [102,301,42,83].max() == 301 19 | assert stringArray.min { it.size() } == 'Java' 20 | assert stringArray.max { it[0] as char } == 'mrhaki' 21 | 22 | // We can even use the Collection GDK methods on an array. 23 | stringArray.eachWithIndex { value, idx -> assert stringArray[idx] == value } 24 | assert stringArray.collect { it.reverse() } == ['ikahrm', 'yvoorG', 'avaJ'] 25 | assert stringArray.find { it =~ /Groovy/ } == 'Groovy' 26 | 27 | // We can remove values with the '-' operator. 28 | assert stringArray - 'mrhaki' == ['Groovy', 'Java'] 29 | 30 | // Other useful methods for arrays. 31 | assert stringArray.reverse() == ['Java', 'Groovy', 'mrhaki'] 32 | assert stringArray.sort() == ['Groovy', 'Java', 'mrhaki'] 33 | assert stringArray.count('mrhaki') == 1 34 | 35 | // Convert to ArrayList. 36 | def strList = stringArray.toList() 37 | assert strList.class.name == 'java.util.ArrayList' 38 | 39 | // Convert ArrayList to array object. 40 | def otherArray = strList as String[] 41 | assert otherArray instanceof String[] 42 | -------------------------------------------------------------------------------- /chapters/collections/collect.groovy: -------------------------------------------------------------------------------- 1 | // Collect without initial collection. 2 | assert (0..3).collect { it * 2 } == [0,2,4,6] 3 | assert [lang: 'Groovy', framework: 'Grails'].collect { it.value } == ['Groovy', 'Grails'] 4 | 5 | // Collect with initial collection argument. 6 | assert [2, 3].collect([0, 1]) { it } == [0, 1, 2, 3] 7 | assert [2, 3].collect([0, 3], { it * 3}) == [0, 3, 6, 9] 8 | assert ['Groovy', 'Grails'].collect(['Gradle']) { it.toLowerCase() } == ['Gradle', 'groovy', 'grails'] 9 | assert [4, -3, 7, 5].collect(['m', 'r']) { (it + 100) as char } == ['m','r','h','a','k','i'] 10 | 11 | 12 | class User { 13 | String name 14 | String aloud() { name.toUpperCase() } 15 | String toString() { name } 16 | } 17 | 18 | def users = [new User(name: 'mrhaki'), new User(name: 'hubert')] 19 | // Compare spread operator and collect method. 20 | assert users*.toString() == ['mrhaki', 'hubert'] 21 | assert users*.aloud() == ['MRHAKI', 'HUBERT'] 22 | assert users.collect { it.toString() } == ['mrhaki', 'hubert'] 23 | assert users.collect { it.aloud() } == ['MRHAKI', 'HUBERT'] 24 | 25 | // Compare collectAll and collect methods. 26 | def list = [10, 20, 30, [1, 2, 3, [25, 50]], ['Groovy']] 27 | assert list.collectAll { it * 2 } == [20, 40, 60, [2, 4, 6, [50, 100]], ['GroovyGroovy']] 28 | assert list.collect { it * 2 } == [20, 40, 60, [1, 2, 3, [25, 50], 1, 2, 3, [25, 50]], ['Groovy', 'Groovy']] 29 | -------------------------------------------------------------------------------- /chapters/collections/combinations.groovy: -------------------------------------------------------------------------------- 1 | def classes = ['Object[]', 'Collection'] 2 | def methods = ['min', 'max'] 3 | 4 | // Make possible combinations of both lists. 5 | def combinations = [classes, methods].combinations() 6 | 7 | assert combinations.size() == 2 * 2 8 | assert combinations == [['Object[]', 'min'], ['Collection', 'min'], ['Object[]', 'max'], ['Collection', 'max']] 9 | assert combinations.findAll { it[0] == 'Collection' }.size() == 2 10 | -------------------------------------------------------------------------------- /chapters/collections/complexkeys.groovy: -------------------------------------------------------------------------------- 1 | def key = 100 // Variable to be used a key. 2 | 3 | def m = [ 4 | (new Date(109, 11, 1)): 'date key', 5 | (-42): 'negative number key', 6 | (false): 'boolean key', 7 | (key): 'variable key' 8 | ] 9 | m.(true) = 'boolean key' // Key is converted to String. 10 | m.(2 + 2) = 'number key' 11 | m[(key + 1)] = 'number key' // Key keeps to be Integer. 12 | 13 | assert m[new Date(109, 11, 1)] == 'date key' 14 | assert m.get(-42) == 'negative number key' 15 | assert m[(false)] == 'boolean key' 16 | assert m[100] == 'variable key' 17 | assert m.getAt(key) == 'variable key' 18 | assert m['true'] == 'boolean key' // Key is String so we can use it to get the value. 19 | assert m.'4' == 'number key' 20 | assert m.get(101) == 'number key' 21 | -------------------------------------------------------------------------------- /chapters/collections/count.groovy: -------------------------------------------------------------------------------- 1 | def list = [0, 1, 2, 1, 1, 3, 2, 0, 1] 2 | 3 | assert list.size() == 9 4 | assert list.count(0) == 2 // Count number of 0's 5 | assert list.count(1) == 4 // Count number of 1's 6 | assert list.count(2) == 2 // Count number of 2's 7 | assert list.count(3) == 1 // Count number of 3's 8 | -------------------------------------------------------------------------------- /chapters/collections/find.groovy: -------------------------------------------------------------------------------- 1 | // Find items in a list in different ways. 2 | def list = ['Daffy', 'Bugs', 'Elmer', 'Tweety', 'Silvester', 'Yosemite'] 3 | assert list.find { it == 'Bugs' } == 'Bugs' 4 | assert list.findAll { it.size() < 6 } == ['Daffy', 'Bugs', 'Elmer'] 5 | assert list.findIndexOf { name -> name =~ /^B.*/ } == 1 // Start with B. 6 | assert list.findIndexOf(3) { it[0] > 'S' } == 3// Use a start index. 7 | assert list.findIndexValues { it =~ /(y|Y)/ } == [0, 3, 5] // Contains y or Y. 8 | assert list.findIndexValues(2) { it =~ /(y|Y)/ } == [3, 5] 9 | assert list.findLastIndexOf { it.size() == 5 } == 2 10 | assert list.findLastIndexOf(1) { it.count('e') > 1 } == 5 11 | assert list.any { it =~ /a/ } 12 | assert list.every { it.size() > 3 } 13 | 14 | // Find items in a map. 15 | def map = [name: 'Messages from mrhaki', url: 'http://mrhaki.blogspot.com', blog: true] 16 | def found = map.find { key, value -> key == 'name' } 17 | assert found.key == 'name' 18 | assert found.value == 'Messages from mrhaki' 19 | found = map.find { it.value =~ /mrhaki/ } 20 | assert found.key == 'name' 21 | assert found.value == 'Messages from mrhaki' 22 | assert map.findAll { key, value -> 23 | value =~ /mrhaki/ 24 | } == [name: 'Messages from mrhaki', url: 'http://mrhaki.blogspot.com'] 25 | assert map.findIndexOf { it.value.endsWith('com') } == 1 26 | assert map.findIndexValues { it.key =~ /l/ } == [1, 2] // All keys with the letter 'l'. 27 | assert map.findLastIndexOf { it.key =~ /l/ && it.value } == 2 28 | assert map.any { entry -> entry.value } 29 | assert map.every { key, value -> key.size() >= 3 } 30 | -------------------------------------------------------------------------------- /chapters/collections/grep.groovy: -------------------------------------------------------------------------------- 1 | // Grep result depends on argument we use. 2 | assert ['test', 12, 20, true].grep(Boolean) == [true] // Class isInstance 3 | assert ['test', 'Groovy', 'Java'].grep(~/^G.*/) == ['Groovy'] // Pattern match 4 | assert ['a', 'b', 'c', 'd'].grep(['b', 'c']) == ['b', 'c'] // List contains 5 | assert [1, 15, 16, 30, 12].grep(12..18) == [15, 16, 12] // Range contains 6 | assert [12.300, 109.20, 42.031, 42.032].grep(42.031) == [42.031] // Object equals 7 | assert [10, 20, 30, 50, 100, 200].grep({ it > 50 }) == [100, 200] // Closure boolean 8 | -------------------------------------------------------------------------------- /chapters/collections/group.groovy: -------------------------------------------------------------------------------- 1 | // A simple map. 2 | def m = [q1: 'Groovy', sort: 'desc', q2: 'Grails'] 3 | 4 | // Closure we use to define the grouping. 5 | // We want all keys starting with 'q' grouped together 6 | // with the key 'params', all other keys are not grouped. 7 | def groupIt = { key, value -> 8 | if (key.startsWith('q')) { 9 | 'params' 10 | } else { 11 | key 12 | } 13 | } 14 | 15 | // Use groupEntriesBy. 16 | def groupEntries = m.groupEntriesBy(groupIt) 17 | assert groupEntries.size() == 2 18 | assert groupEntries.params && groupEntries.sort 19 | assert groupEntries.sort[0].value == 'desc' // Key for a list of Map$Entry objects. 20 | assert groupEntries.params.size() == 2 21 | assert groupEntries.params[0].value == 'Groovy' 22 | assert groupEntries.params[0].key == 'q1' 23 | assert groupEntries.params.find { it.key == 'q2' }.value == 'Grails' 24 | assert groupEntries.params instanceof ArrayList 25 | assert groupEntries.params[0] instanceof Map$Entry 26 | 27 | // Use groupBy. 28 | def group = m.groupBy(groupIt) 29 | assert group.size() == 2 30 | assert group.params && group.sort 31 | assert group.sort.sort == 'desc' // Key for Map with key/value pairs. 32 | assert group.params.size() == 2 33 | assert group.params.q1 == 'Groovy' 34 | assert group.params.keySet().toArray()[0] == 'q1' 35 | assert group.params.q2 == 'Grails' 36 | assert group.params instanceof Map 37 | assert group.params.q1 instanceof String 38 | 39 | 40 | class User { 41 | String name 42 | String city 43 | Date birthDate 44 | public String toString() { "$name" } 45 | } 46 | 47 | def users = [ 48 | new User(name:'mrhaki', city: 'Tilburg', birthDate: new Date(year: 73, month: 9, date: 7)), 49 | new User(name:'bob', city: 'New York', birthDate: new Date(year: 63, month: 3, date: 30)), 50 | new User(name:'britt', city: 'Amsterdam', birthDate: new Date(year: 80, month: 5, date: 12)), 51 | new User(name:'kim', city: 'Amsterdam', birthDate: new Date(year: 83, month: 3, date: 30)), 52 | new User(name:'liam', city: 'Tilburg', birthDate: new Date(year: 109, month: 3, date: 6)) 53 | ] 54 | 55 | // Helper closure for asserts. 56 | def userToString = { it.toString() } 57 | 58 | // Group by city property of user object: 59 | def usersByCity = users.groupBy({ user -> user.city }) 60 | assert usersByCity["Tilburg"].size() == 2 61 | assert usersByCity["Tilburg"].collect(userToString) == ['mrhaki', 'liam'] 62 | assert usersByCity["New York"].collect(userToString) == ['bob'] 63 | assert usersByCity["Amsterdam"].collect(userToString) == ['britt', 'kim'] 64 | 65 | // Group by year of birthdate property of user object: 66 | def byYear = { u -> u.birthDate[Calendar.YEAR] } 67 | def usersByBirthDateYear = users.groupBy(byYear) 68 | assert usersByBirthDateYear[1973].collect(userToString) == ['mrhaki'] 69 | 70 | // Just a little fun with the closure: 71 | def groupByGroovy = { 72 | if (it =~ /y/) { 73 | "Contains y" 74 | } else { 75 | "Doesn't contain y" 76 | } 77 | } 78 | assert ['Groovy', 'Java', 'Scala'].groupBy(groupByGroovy) == ["Contains y":["Groovy"], "Doesn't contain y":["Java", "Scala"]] 79 | -------------------------------------------------------------------------------- /chapters/collections/headtail.groovy: -------------------------------------------------------------------------------- 1 | def list = [1, 2, 3, 4] 2 | 3 | // Recursive method with tail/head methods. 4 | def reverse(l) { 5 | if (l.size() == 0) { 6 | [] 7 | } else { 8 | reverse(l.tail()) + l.head() 9 | } 10 | } 11 | 12 | assert reverse(list) == [4, 3, 2, 1] 13 | 14 | // For the same result we can of course use the List.reverse() method, 15 | // but then we didn't learn about tail() and head() ;-) 16 | assert list.reverse() == [4, 3, 2, 1] 17 | 18 | // Tail/head also works on arrays. 19 | String[] stringArray = ['Groovy', 'Grails', 'Spock'] 20 | assert stringArray.head() == 'Groovy' 21 | assert stringArray.tail() == ['Grails', 'Spock'] 22 | assert stringArray.first() == 'Groovy' 23 | assert stringArray.last() == 'Spock' 24 | -------------------------------------------------------------------------------- /chapters/collections/immutable.groovy: -------------------------------------------------------------------------------- 1 | def list = ['Groovy', 'Java', 'JRuby'].asImmutable() 2 | assert list[0] == 'Groovy' 3 | try { 4 | list << 'Scala' // Cannot add item. 5 | } catch (e) { 6 | assert e instanceof UnsupportedOperationException 7 | } 8 | try { 9 | list.remove 'Java' // Cannot remove item. 10 | } catch (e) { 11 | assert e instanceof UnsupportedOperationException 12 | } 13 | 14 | def map = [name: 'mrhaki', country: 'The Netherlands', blog: true].asImmutable() 15 | assert map.blog 16 | try { 17 | map.blog = false // Cannot change item. 18 | } catch (e) { 19 | assert e instanceof UnsupportedOperationException 20 | } 21 | -------------------------------------------------------------------------------- /chapters/collections/inject.groovy: -------------------------------------------------------------------------------- 1 | // Traditional "sum of the values in a list" sample. 2 | // First with each() and side effect, because we have 3 | // to declare a variable to hold the result: 4 | def total = 0 5 | (1..4).each { total += it } 6 | assert total == 10 7 | 8 | // With the inject method we 'inject' the 9 | // first value of the result, and then for 10 | // each item the result is increased and 11 | // returned for the next iteration. 12 | def sum = (1..4).inject(0) { result, i -> result + i } 13 | assert sum == 10 14 | 15 | // We add a println statement to see what happens. 16 | (1..4).inject(0) { result, i -> 17 | println "$result + $i = ${result + i}" 18 | result + i 19 | } 20 | // Output: 21 | // 0 + 1 = 1 22 | // 1 + 2 = 3 23 | // 3 + 3 = 6 24 | // 6 + 4 = 10 25 | 26 | 27 | class Person { 28 | String username 29 | String email 30 | } 31 | def persons = [ 32 | new Person(username:'mrhaki', email: 'email@host.com'), 33 | new Person(username:'hubert', email: 'other@host.com') 34 | ] 35 | 36 | // Convert list to a map where the key is the value of 37 | // username property of Person and the value is the email 38 | // property of Person. We inject an empty map as the starting 39 | // point for the result. 40 | def map = persons.inject([:]) { result, person -> 41 | result[person.username] = person.email 42 | result // Remember to return the map as result. 43 | } 44 | assert map == [mrhaki: 'email@host.com', hubert: 'other@host.com'] 45 | -------------------------------------------------------------------------------- /chapters/collections/intersect.groovy: -------------------------------------------------------------------------------- 1 | def one = ['Java', 'Groovy', 'Scala'] 2 | def two = ['Groovy', 'JRuby', 'Java'] 3 | def three = ['C++', 'C##'] 4 | 5 | assert one.intersect(two) == ['Groovy', 'Java'] 6 | assert one.intersect(three) == [] 7 | assert one.disjoint(three) 8 | assert !one.disjoint(two) 9 | 10 | 11 | def m1 = [a: 'Groovy', b: 'rocks', c: '!'] 12 | def m2 = [a: 'Groovy', b: 'rocks', c: '?', d: 'Yes!'] 13 | 14 | assert [a: 'Groovy', b: 'rocks'] == m1.intersect(m2) 15 | 16 | // Numbers are compared for equality and value from 17 | // first map is used as intersection result map. 18 | assert [1: 1.0, 2: 2].intersect([1: 1, 2: 2.0]) == [1: 1.0, 2: 2] 19 | -------------------------------------------------------------------------------- /chapters/collections/join.groovy: -------------------------------------------------------------------------------- 1 | def abc = ['a', 'b', 'c'] 2 | assert abc.join() == 'abc' 3 | assert abc.join('::') == 'a::b::c' 4 | 5 | def numbers = [0, 1, 2, 3, 4, 5] as Integer[] 6 | assert numbers.join() == '012345' 7 | assert numbers.join(' x ') == '0 x 1 x 2 x 3 x 4 x 5' 8 | assert numbers.join(' ') == '0 1 2 3 4 5' 9 | 10 | // Object toString() method is invoked when necessary. 11 | def objects = [new URL('http://www.mrhaki.com'), 'mrhaki', new Expando(name: 'mrhaki'), new Date(110, 10, 10)] 12 | assert objects.join(',') == 'http://www.mrhaki.com,mrhaki,{name=mrhaki},Wed Nov 10 00:00:00 CET 2010' 13 | 14 | // Also great for creating URL query parameters. 15 | def map = [q: 'groovy', maxResult: 10, start: 0, format: 'xml'] 16 | def params = map.inject([]) { result, entry -> 17 | result << "${entry.key}=${URLEncoder.encode(entry.value.toString())}" 18 | }.join('&') 19 | assert params == 'q=groovy&maxResult=10&start=0&format=xml' 20 | -------------------------------------------------------------------------------- /chapters/collections/listasstring.groovy: -------------------------------------------------------------------------------- 1 | def list = ['Groovy', 'Clojure', 'Java'] 2 | 3 | assert list.toListString() == '[Groovy, Clojure, Java]' // Just as list.toString() 4 | assert list.toString() == '[Groovy, Clojure, Java]' 5 | assert list.toListString(12) == '[Groovy, Clojure, ...]' 6 | -------------------------------------------------------------------------------- /chapters/collections/listtomap.groovy: -------------------------------------------------------------------------------- 1 | def list = ['key', 'value', 'name', 'mrhaki'] as Object[] 2 | def map = list.toSpreadMap() 3 | 4 | assert map.size() == 2 5 | assert map.key == 'value' 6 | assert map['name'] == 'mrhaki' 7 | -------------------------------------------------------------------------------- /chapters/collections/loops.groovy: -------------------------------------------------------------------------------- 1 | // Result variable for storing loop results. 2 | def result = '' 3 | // Closure to fill result variable with value. 4 | def createResult = { 5 | if (!it) { // A bit of Groovy truth: it == 0 is false 6 | result = '0' 7 | } else { 8 | result += it 9 | } 10 | } 11 | 12 | // Classic for loop. 13 | for (i = 0; i < 5; i++) { 14 | createResult(i) 15 | } 16 | assert result == '01234' 17 | 18 | // Using int.upto(max). 19 | 0.upto(4, createResult) 20 | assert result == '01234' 21 | 22 | // Using int.times. 23 | 5.times(createResult) 24 | assert result == '01234' 25 | 26 | // Using int.step(to, increment). 27 | 0.step 5, 1, createResult 28 | assert result == '01234' 29 | 30 | // Classic while loop. 31 | def z = 0 32 | while (z < 5) { 33 | createResult(z) 34 | z++ 35 | } 36 | assert result == '01234' 37 | 38 | def list = [0, 1, 2, 3, 4] 39 | 40 | // Classic Java for-each loop. 41 | for (int i : list) { 42 | createResult(i) 43 | } 44 | assert result == '01234' 45 | 46 | // Groovy for-each loop. 47 | for (i in list) { 48 | createResult(i) 49 | } 50 | assert result == '01234' 51 | 52 | // Use each method to loop through list values. 53 | list.each(createResult) 54 | assert result == '01234' 55 | 56 | // Ranges are lists as well. 57 | (0..4).each(createResult) 58 | assert result == '01234' 59 | 60 | // eachWithIndex can be used with closure: first parameter is value, second is index. 61 | result = '' 62 | list.eachWithIndex { listValue, index -> result += "$index$listValue" } 63 | assert result == '0011223344' 64 | -------------------------------------------------------------------------------- /chapters/collections/mapasinterface.groovy: -------------------------------------------------------------------------------- 1 | def baseDir = new File('interfaces') 2 | baseDir.mkdir() 3 | ('a'..'e').each { 4 | def file = new File(baseDir, "${it}.txt") 5 | file << 'sample' 6 | } 7 | 8 | def map = [ 9 | // Implement FileFilter.accept(File) method. 10 | accept: { file -> file.name ==~ /.*\.txt$/ } 11 | ] as FileFilter 12 | assert baseDir.listFiles(map).size() == 5 13 | -------------------------------------------------------------------------------- /chapters/collections/mapdefaultvalues.groovy: -------------------------------------------------------------------------------- 1 | // Simple map. 2 | def m = [name: 'mrhaki', language: 'Groovy'] 3 | 4 | assert m.getAt('name') == 'mrhaki' 5 | assert m['name'] == 'mrhaki' 6 | assert m.language == 'Groovy' 7 | assert m."name" == 'mrhaki' 8 | assert m.get('name') == 'mrhaki' // We can omit the default value if we know the key exists. 9 | assert m.get('language', 'Java') == 'Groovy' 10 | assert m.get('expression') == null // Non-existing key in map. 11 | assert m.get('expression', 'rocks') == 'rocks' // Use default value, this also creates the key/value pair in the map. 12 | assert m.get('expression') == 'rocks' 13 | assert m == [name: 'mrhaki', language: 'Groovy', expression: 'rocks'] 14 | 15 | 16 | def map = [start: 'one'].withDefault { key -> 17 | key.isNumber() ? 42 : 'Groovy rocks!' 18 | } 19 | 20 | assert map.start == 'one' 21 | assert map['1'] == 42 22 | assert map['I say'] == 'Groovy rocks!' 23 | assert map.size() == 3 24 | 25 | // We can still assign our own values to keys of course: 26 | map['mrhaki'] = 'Hubert Klein Ikkink' 27 | assert map.mrhaki == 'Hubert Klein Ikkink' 28 | assert map.size() == 4 29 | -------------------------------------------------------------------------------- /chapters/collections/orderby.groovy: -------------------------------------------------------------------------------- 1 | class Language { 2 | String name 3 | boolean dynamic 4 | String toString() { "name: $name, dynamic: $dynamic" } 5 | } 6 | 7 | def languages = [ 8 | new Language(name: 'Groovy', dynamic: true), 9 | new Language(name: 'Java', dynamic: false), 10 | new Language(name: 'Clojure', dynamic: true) 11 | ] 12 | 13 | // We order first on dynamic property and then name property. 14 | def orderByDynamicAndName = new OrderBy([{ it.dynamic }, { it.name }]) 15 | def sortedLanguages = languages.sort(orderByDynamicAndName) 16 | 17 | assert sortedLanguages[0].name == 'Java' 18 | assert !sortedLanguages[0].dynamic 19 | assert sortedLanguages[1].name == 'Clojure' 20 | assert sortedLanguages[2].name == 'Groovy' 21 | assert sortedLanguages[1].dynamic && sortedLanguages[2].dynamic 22 | -------------------------------------------------------------------------------- /chapters/collections/permutations.groovy: -------------------------------------------------------------------------------- 1 | def languages = ['Groovy', 'Clojure', 'Scala'] 2 | 3 | def result = [] 4 | languages.eachPermutation { 5 | result << it 6 | } 7 | 8 | assert result.size() == 6 9 | assert result[0] == ['Groovy', 'Clojure', 'Scala'] 10 | assert result[1] == ['Groovy', 'Scala', 'Clojure'] 11 | assert result.findAll { it[0] == 'Clojure' } == [['Clojure', 'Groovy', 'Scala'], ['Clojure', 'Scala', 'Groovy']] 12 | 13 | // We can also get the complete list of permutations as Set. 14 | def list = [true, false] 15 | def permutations = list.permutations() 16 | assert permutations.size() == 2 17 | assert permutations == [[false,true], [true,false]] as Set 18 | -------------------------------------------------------------------------------- /chapters/collections/ranges.groovy: -------------------------------------------------------------------------------- 1 | // Simple ranges with number values. 2 | def ints = 1..10 3 | assert ints == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 4 | assert ints.size() == 10 5 | assert ints.from == 1 6 | assert ints.to == 10 7 | 8 | // We can step through the values. 9 | def list = [] 10 | ints.step(2) { list << it } 11 | assert list == [1, 3, 5, 7, 9] 12 | 13 | // A range is just a List. 14 | assert ints[0] == 1 15 | assert ints.last() == 10 16 | def s = '' 17 | (2..4).each { s += it } 18 | assert s == '234' 19 | 20 | // Exclusive range. 21 | def exclusive = 2..<8 22 | assert exclusive == [2, 3, 4, 5, 6, 7] 23 | assert exclusive.size() == 6 24 | assert !exclusive.contains(8) 25 | 26 | // Object with next() and previous() can be used 27 | // in ranges. Groovy extends Java enum with 28 | // next() and previous() so we can use it in ranges. 29 | enum Compass { 30 | NORTH, NORTH_EAST, EAST, SOUTH_EAST, 31 | SOUTH, SOUTH_WEST, WEST, NORTH_WEST 32 | } 33 | def northToSouth = Compass.NORTH..Compass.SOUTH 34 | assert northToSouth.size() == 5 35 | assert northToSouth[2] == Compass.EAST 36 | assert northToSouth.contains(Compass.SOUTH_EAST) 37 | 38 | // Bonus: next() and previous() are equivalent to 39 | // ++ and -- operators. 40 | def region = Compass.SOUTH 41 | assert ++region == Compass.SOUTH_WEST 42 | assert --region == Compass.SOUTH 43 | -------------------------------------------------------------------------------- /chapters/collections/reverse.groovy: -------------------------------------------------------------------------------- 1 | def list = [10, 20, 30] 2 | 3 | assert list.reverse() == [30, 20, 10] 4 | assert list[-1..0] == [30, 20, 10] 5 | 6 | def result = [] 7 | list.reverseEach { 8 | result << it * 2 9 | } 10 | 11 | assert result == [60, 40, 20] 12 | 13 | 14 | def reversed = [:] 15 | [a: 1, c: 3, b: 2].reverseEach { key, value -> 16 | reversed[key] = value ** 2 17 | } 18 | 19 | assert reversed == [b: 4, c: 9, a: 1] 20 | 21 | // TreeMap uses natural ordering of keys, so 22 | // reverseEach starts with key 'c'. 23 | def tree = [a: 10, c: 30, b: 20] as TreeMap 24 | def reversedMap = [:] 25 | tree.reverseEach { 26 | reversedMap[it.key] = it.value * 2 27 | } 28 | assert reversedMap == [c: 60, b: 40, a: 20] 29 | -------------------------------------------------------------------------------- /chapters/collections/sortmap.groovy: -------------------------------------------------------------------------------- 1 | def m = [sort: 'asc', name: 'test', paginate: true, max: 100] 2 | 3 | def expectedKeys = ['max', 'name', 'paginate', 'sort'] 4 | assert m.sort()*.key == expectedKeys 5 | assert m.sort( { k1, k2 -> k1 <=> k2 } as Comparator )*.key == expectedKeys 6 | 7 | // Other ways to sort a map. 8 | assert new TreeMap(m)*.key == expectedKeys 9 | assert m.sort { e1, e2 -> e1.key <=> e2.key }*.key == expectedKeys // Sort by closure. 10 | -------------------------------------------------------------------------------- /chapters/collections/split.groovy: -------------------------------------------------------------------------------- 1 | def range = 1..10 2 | def (even, odd) = range.split { it % 2 == 0 } 3 | assert even == [2,4,6,8,10] 4 | assert odd == [1,3,5,7,9] 5 | 6 | def map = [language: 'Groovy', username: 'mrhaki', age: 36] 7 | def (strings, others) = map.split { it.value instanceof String } 8 | assert strings.size() == 2 9 | assert others.size() == 1 10 | assert strings[0].key == 'language' 11 | assert strings[0].value == 'Groovy' 12 | assert strings[1].key == 'username' 13 | assert strings[1].value == 'mrhaki' 14 | assert others[0].key == 'age' 15 | assert others[0].value == 36 16 | -------------------------------------------------------------------------------- /chapters/collections/submap.groovy: -------------------------------------------------------------------------------- 1 | def map = [name: 'mrhaki', country: 'The Netherlands', blog: true, languages: ['Groovy', 'Java']] 2 | 3 | def keys = ['name', 'blog'] 4 | assert map.subMap(keys) == [name: 'mrhaki', blog: true] 5 | 6 | def booleanKeys = map.findAll { it.value instanceof Boolean }.collect { it.key } 7 | assert map.subMap(booleanKeys) == [blog: true] 8 | 9 | def words = ['a': 'Apple', 'j': 'Java', 'g': 'Groovy', 'c': 'Cool'] 10 | def range = 'c'..'h' // Range is also a list and can be used here. 11 | def rangeWords = words.subMap(range).findAll{ it.value } 12 | // words.subMap(range) returns [c:Cool, d:null, e:null, f:null, g:Groovy, h:null] 13 | // so we use the findAll method to filter out all null values. 14 | assert rangeWords == ['c': 'Cool', 'g': 'Groovy'] 15 | -------------------------------------------------------------------------------- /chapters/collections/subscript.groovy: -------------------------------------------------------------------------------- 1 | def list = ['Groovy', 'Grails', 'Griffon', 'Gradle'] 2 | def iterator = list.iterator() 3 | 4 | assert iterator[0] == 'Groovy' 5 | assert iterator[-1] == 'Gradle' 6 | assert !iterator[1] // Iterator is exhausted. 7 | 8 | iterator = list.iterator() // Get new iterator. 9 | def newList = [] 10 | (0.. 4 | (1..5).each { 5 | def file = new File("file${it}.${ext}") 6 | file << 'sample' 7 | } 8 | } 9 | 10 | // We can use ANT style include/exclude file patterns. 11 | def includeFiles = '**/*.txt' 12 | def excludeFiles = '**/*.doc' 13 | def txtFiles = new FileNameFinder().getFileNames('.', includeFiles, excludeFiles) 14 | assert new File('file3.txt').absolutePath in txtFiles 15 | 16 | // Use FileNameByRegexFinder to search with a regular expression. 17 | // In this sample we look for files ending with .doc extension. 18 | def docFiles = new FileNameByRegexFinder().getFileNames('.', /.*\.doc$/) 19 | assert new File('.', 'file2.doc').absolutePath in docFiles 20 | -------------------------------------------------------------------------------- /chapters/files/filetypes.groovy: -------------------------------------------------------------------------------- 1 | import groovy.io.FileType 2 | 3 | // First create sample dirs and files. 4 | def baseDir = new File('filetypes') 5 | baseDir.mkdir() 6 | (1..3).each { 7 | new File(baseDir, "dir$it").mkdir() 8 | } 9 | (1..3).each { 10 | def file = new File(baseDir, "file$it") 11 | file << "Sample content for ${file.absolutePath}" 12 | } 13 | 14 | def currentDir = baseDir 15 | def dirs = [] 16 | // Use FileType.DIRECTORIES to only work with directories. 17 | currentDir.eachFile FileType.DIRECTORIES, { 18 | dirs << it.name 19 | } 20 | assert dirs.join(',') == 'dir1,dir2,dir3' 21 | 22 | def files = [] 23 | // Use FileType.FILES to only work with files. 24 | currentDir.eachFileMatch(FileType.FILES, ~/file\d/) { 25 | files << it.name 26 | } 27 | assert files.join(',') == 'file1,file2,file3' 28 | 29 | def found = [] 30 | // FileType.ANY works with both directories and files. 31 | currentDir.eachFileMatch(FileType.ANY, ~/.*2/) { 32 | found << it.name 33 | } 34 | 35 | assert found.join(',') == 'dir2,file2' 36 | -------------------------------------------------------------------------------- /chapters/files/operators.groovy: -------------------------------------------------------------------------------- 1 | // Normal way of creating file objects. 2 | def file1 = new File('groovy1.txt') 3 | def file2 = new File('groovy2.txt') 4 | def file3 = new File('groovy3.txt') 5 | 6 | // Writing to the files with the write method: 7 | file1.write 'Working with files the Groovy way is easy.\n' 8 | 9 | // Using the leftShift operator: 10 | file1 << 'See how easy it is to add text to a file.\n' 11 | 12 | // Using the text property: 13 | file2.text = '''We can even use the text property of 14 | a file to set a complete block of text at once.''' 15 | 16 | // Or a writer object: 17 | file3.withWriter('UTF-8') { writer -> 18 | writer.write('We can also use writers to add contents.') 19 | } 20 | 21 | // Reading contents of files to an array: 22 | def lines = file1.readLines() 23 | assert 2 == lines.size() 24 | assert lines[0] == 'Working with files the Groovy way is easy.' 25 | 26 | // Or we read with the text property: 27 | assert file3.text == 'We can also use writers to add contents.' 28 | 29 | // Or with a reader: 30 | count = 0 31 | file2.withReader { reader -> 32 | while (line = reader.readLine()) { 33 | switch (count) { 34 | case 0: 35 | assert line == 'We can even use the text property of' 36 | break 37 | case 1: 38 | assert line == 'a file to set a complete block of text at once.' 39 | break 40 | } 41 | count++ 42 | } 43 | } 44 | 45 | // We can also read contents with a filter: 46 | sw = new StringWriter() 47 | file1.filterLine(sw) { it =~ /Groovy/ } 48 | assert sw.toString() == 'Working with files the Groovy way is easy.\n' 49 | 50 | // We can look for files in the directory with different methods. 51 | // See for a complete list the File GDK documentation. 52 | files = [] 53 | new File('.').eachFileMatch(~/^groovy.*\.txt$/) { files << it.name } 54 | assert files == ['groovy1.txt', 'groovy2.txt', 'groovy3.txt'] 55 | 56 | // Delete all files: 57 | files.each { new File(it).delete() } 58 | -------------------------------------------------------------------------------- /chapters/files/rename.groovy: -------------------------------------------------------------------------------- 1 | def file = new File('test.groovy') 2 | file.text = 'simple content' 3 | file.renameTo 'newname.groovy' 4 | 5 | def newFile = new File('newname.groovy') 6 | assert newFile.exists() 7 | assert newFile.text == 'simple content' 8 | -------------------------------------------------------------------------------- /chapters/files/traversing.groovy: -------------------------------------------------------------------------------- 1 | import static groovy.io.FileType.* 2 | 3 | // We build a sample dir structure so we can 4 | // test the traversing. 5 | def basedir = new File('traverse-sample') 6 | basedir.mkdirs() 7 | (1..15).each { counter -> 8 | def subdir = new File(basedir, "sub$counter") 9 | subdir.mkdirs() 10 | ['txt', 'doc'].each { ext -> 11 | def file = new File(subdir, "sample${counter}.$ext") 12 | file.write 'sample' 13 | } 14 | } 15 | 16 | def countFilesAndDirs = 0 17 | basedir.traverse { 18 | countFilesAndDirs++ 19 | } 20 | assert countFilesAndDirs == 45 21 | 22 | def totalFileSize = 0 23 | def txtFileCount = 0 24 | def sumFileSize = { 25 | totalFileSize += it.size() 26 | txtFileCount++ 27 | } 28 | def filterTxtFiles = ~/.*\.txt$/ 29 | basedir.traverse type: FILES, visit: sumFileSize, nameFilter: filterTxtFiles 30 | assert txtFileCount == 15 31 | assert totalFileSize == 90 32 | 33 | def dirsWithSmallFiles = [] 34 | def countSmallFiles = 0 35 | def postDirVisitor = { 36 | if (countSmallFiles > 0) { 37 | dirsWithSmallFiles << it.name 38 | } 39 | countSmallFiles = 0 40 | } 41 | 42 | basedir.traverse(type: FILES, postDir: postDirVisitor, nameFilter: ~/.*\.doc/) { 43 | if (it.name.size() < 12) { 44 | countSmallFiles++ 45 | } 46 | } 47 | assert dirsWithSmallFiles == ['sub1', 'sub2', 'sub3', 'sub4', 'sub5', 'sub6', 'sub7', 'sub8', 'sub9'] 48 | -------------------------------------------------------------------------------- /chapters/files/withwriter.groovy: -------------------------------------------------------------------------------- 1 | def file = new File('sample.txt') 2 | 3 | file.withWriter('UTF-8') { 4 | it.writeLine 'Adding this text to the file.' 5 | } 6 | 7 | def s 8 | file.withReader('UTF-8') { 9 | s = it.readLine() 10 | } 11 | 12 | assert s == 'Adding this text to the file.' 13 | -------------------------------------------------------------------------------- /chapters/metaclass/addmethods.groovy: -------------------------------------------------------------------------------- 1 | // We add the method rightShift to the List class. 2 | // The implementation is simply calling the remove method of List with 3 | // the provided argument. 4 | List.metaClass.rightShift { 5 | delegate.remove it 6 | } 7 | 8 | def list = ['one', 'two', 'three', 'four'] 9 | assert list.size() == 4 10 | 11 | list.rightShift 'two' 12 | assert list.size() == 3 13 | assert list == ['one', 'three', 'four'] 14 | 15 | // Operator overloading in action: rightShift is >> 16 | list >> 'one' 17 | assert list.size() == 2 18 | assert list == ['three', 'four'] 19 | 20 | 21 | // We can also add behaviour to a specific instance instead of class. 22 | // Notice we use the instance list instead of class List to assign 23 | // method groovy to metaClass property. 24 | list.metaClass.groovy { 25 | delegate.collect { it + ' groovy' } 26 | } 27 | 28 | assert list.groovy() == ['three groovy', 'four groovy'] 29 | 30 | def newList = ['a', 'b'] 31 | try { 32 | newList.groovy() // groovy method was added to list instance not List class. 33 | assert false 34 | } catch (e) { 35 | assert e instanceof MissingMethodException 36 | } 37 | -------------------------------------------------------------------------------- /chapters/metaclass/getproperty.groovy: -------------------------------------------------------------------------------- 1 | class User { 2 | String username 3 | } 4 | 5 | User.metaClass.getProperty = { String propName -> 6 | def meta = User.metaClass.getMetaProperty(propName) 7 | if (meta) { 8 | meta.getProperty(delegate) 9 | } else { 10 | 'Dynamic property for User' 11 | } 12 | } 13 | 14 | def mrhaki = new User(username: 'mrhaki') 15 | 16 | assert mrhaki.username == 'mrhaki' 17 | assert mrhaki.fullname == 'Dynamic property for User' 18 | -------------------------------------------------------------------------------- /chapters/metaclass/metadsl.groovy: -------------------------------------------------------------------------------- 1 | // DSL for defining multiple methods with different 2 | // arguments in a closure. 3 | String.metaClass { 4 | or << { String s -> delegate.plus(' or ').plus(s) } 5 | or << { List l -> delegate.findAll("(${l.join('|')})") } 6 | and { String s -> delegate.plus(' and ').plus(s) } 7 | 'static' { 8 | groovy { 'Yeah man!' } 9 | } 10 | } 11 | 12 | assert ("Groovy" | "Java?") == 'Groovy or Java?' 13 | assert ("Groovy" | ['o', 'y']) == ['o', 'o', 'y'] 14 | assert ("Groovy" & "Java!") == 'Groovy and Java!' 15 | 16 | assert String.groovy() == 'Yeah man!' 17 | -------------------------------------------------------------------------------- /chapters/metaclass/methodarguments.groovy: -------------------------------------------------------------------------------- 1 | // Define 'groovy' method with different argument types. 2 | String.metaClass.groovy << { Integer number -> 3 | delegate * number 4 | } << { String s -> 5 | delegate + s 6 | } << { -> 7 | delegate + ' Groovy rocks.' 8 | } 9 | 10 | assert 'Groovy'.groovy(2) == 'GroovyGroovy' 11 | assert 'Hello world'.groovy(' from Groovy') == 'Hello world from Groovy' 12 | assert 'It is true.'.groovy() == 'It is true. Groovy rocks.' 13 | -------------------------------------------------------------------------------- /chapters/metaclass/override.groovy: -------------------------------------------------------------------------------- 1 | // For non-existing methods we can use << or = 2 | // to define the method. 3 | Integer.metaClass.eights << { delegate * 8 } 4 | Integer.metaClass.hundreds = { delegate * 100 } 5 | assert 4.eights() == 32 6 | assert 2.hundreds() == 200 7 | 8 | try { 9 | Integer.metaClass.toString << { 'Groovy' } // Cannot use << for existing method. 10 | assert false 11 | } catch (e) { 12 | assert e.message == 'Cannot add new method [toString] for arguments [[]]. It already exists!' 13 | } 14 | 15 | // For an existing method we must = to define new implementation. 16 | Integer.metaClass.toString = { 'Groovy' } 17 | assert 100.toString() == 'Groovy' 18 | -------------------------------------------------------------------------------- /chapters/metaclass/staticmethod.groovy: -------------------------------------------------------------------------------- 1 | class User { 2 | String username, email 3 | String toString() { "$username, $email" } 4 | } 5 | 6 | // Add 'new' static method to User class. 7 | User.metaClass.static.new = { u, e -> 8 | new User(username: u, email: e) 9 | } 10 | 11 | def u = User.new('mrhaki', 'mail@host.com') 12 | 13 | assert u.username == 'mrhaki' 14 | assert u.email == 'mail@host.com' 15 | -------------------------------------------------------------------------------- /chapters/numbers/absolute.groovy: -------------------------------------------------------------------------------- 1 | // Make a number value absolute. 2 | assert (-42).abs() == 42 3 | assert (-43.21).abs() == 43.21 4 | -------------------------------------------------------------------------------- /chapters/numbers/integerdivision.groovy: -------------------------------------------------------------------------------- 1 | def x = 26 2 | def y = 10 3 | 4 | def resultDiv = x.div(y) // or x / y 5 | def resultIntDiv = x.intdiv(y) // Integer division 6 | 7 | assert resultDiv == 2.6 8 | assert resultIntDiv == 2 9 | assert resultDiv.class == java.math.BigDecimal 10 | assert resultIntDiv.class == java.lang.Integer 11 | -------------------------------------------------------------------------------- /chapters/numbers/power.groovy: -------------------------------------------------------------------------------- 1 | // Raise the power of a number. 2 | assert 3**2 == 9 // Using the operator ** 3 | assert 3.power(3) == 27 // Using the power() method. 4 | assert Math.pow(2, 4) == 16 // Java way. 5 | -------------------------------------------------------------------------------- /chapters/numbers/round.groovy: -------------------------------------------------------------------------------- 1 | def doubleValue = 12.5456d 2 | 3 | assert doubleValue.round(3) == 12.546d 4 | assert doubleValue.round() == 13 5 | 6 | assert doubleValue.trunc() == 12 7 | assert doubleValue.trunc(2) == 12.54d 8 | 9 | def floatValue = 987.654f 10 | 11 | assert floatValue.round(2) == 987.65f 12 | assert floatValue.round() == 988 13 | assert floatValue.trunc(1) == 987.6f 14 | assert floatValue.trunc() == 987 15 | -------------------------------------------------------------------------------- /chapters/regexp/matcher.groovy: -------------------------------------------------------------------------------- 1 | def finder = ('groovy' =~ /gr.*/) 2 | assert finder instanceof java.util.regex.Matcher 3 | 4 | def matcher = ('groovy' ==~ /gr.*/) 5 | assert matcher instanceof Boolean 6 | 7 | assert 'Groovy rocks!' =~ /Groovy/ // =~ in conditional context returns boolean. 8 | assert !('Groovy rocks!' ==~ /Groovy/) // ==~ looks for an exact match. 9 | assert 'Groovy rocks!' ==~ /Groovy.*/ 10 | 11 | def cool = /gr\w{4}/ // Start with gr followed by 4 characters. 12 | def findCool = ('groovy, java and grails rock!' =~ /$cool/) 13 | assert findCool.count == 2 14 | assert findCool.size() == 2 // Groovy adds size() method. 15 | assert findCool[0] == 'groovy' // Array-like access to match results. 16 | assert findCool.getAt(1) == 'grails' 17 | 18 | // With grouping we get a multidimensional array. 19 | def group = ('groovy and grails, ruby and rails' =~ /(\w+) and (\w+)/) 20 | assert group.hasGroup() 21 | assert group.size() == 2 22 | assert group[0] == ['groovy and grails', 'groovy', 'grails'] 23 | assert group[1][2] == 'rails' 24 | 25 | // Use matcher methods. 26 | assert ('Hello world' =~ /Hello/).replaceFirst('Hi') == 'Hi world' 27 | 28 | // Groovy matcher syntax can be used in other methods. 29 | assert ['def', 'abc', '123'].findAll { it =~ /abc/ } == ['abc'] 30 | assert ['def', 'abc', '123'].collect { it ==~ /\d{3}/ } == [false, false, true] 31 | -------------------------------------------------------------------------------- /chapters/regexp/pattern.groovy: -------------------------------------------------------------------------------- 1 | def single = ~'[ab]test\\d' // Notice the space between = and ~. 2 | assert single.class.name == 'java.util.regex.Pattern' 3 | 4 | def dubble = ~"string\$" 5 | assert dubble instanceof java.util.regex.Pattern 6 | 7 | // Groovy's String slashy syntax is very useful to 8 | // define patterns, because we don't have to escape 9 | // all those backslashes. 10 | def slashy = ~/slashy \d+ value/ 11 | assert slashy instanceof java.util.regex.Pattern 12 | 13 | // GString adds a negate() method which is mapped 14 | // to the ~ operator. 15 | def negateSlashy = /${'hello'}GString$/.negate() 16 | assert negateSlashy instanceof java.util.regex.Pattern 17 | def s = 'more' 18 | def curlySlashy = ~"$s GString" 19 | assert curlySlashy instanceof java.util.regex.Pattern 20 | 21 | // Using Pattern.matcher() to create new java.util.regex.Matcher. 22 | def testPattern = ~'t..t' 23 | assert testPattern.matcher("test").matches() 24 | 25 | // Groovy adds isCase() method to Pattern class. 26 | // Easy for switch and grep statements. 27 | def p = ~/\w+vy$/ // Ends with 'vy', like 'groovy'. 28 | assert p.isCase('groovy') 29 | 30 | switch ('groovy') { 31 | case ~/java/: assert false; break; 32 | case ~/gr\w{4}/: assert true; break; 33 | default: assert false 34 | } 35 | 36 | // We can use flags in our expressions. In this sample 37 | // we use the case insensitive flag (?i). 38 | // And the grep method accepts Patterns. 39 | def lang = ~/^(?i)gr.*/ 40 | def languages = ['java', 'Groovy', 'gRails'] 41 | assert languages.grep(lang) == ['Groovy', 'gRails'] 42 | -------------------------------------------------------------------------------- /chapters/strings/base64.groovy: -------------------------------------------------------------------------------- 1 | def s = 'Argh, Groovy you say, mate?' 2 | 3 | // Encode. 4 | String encoded = s.bytes.encodeBase64().toString() 5 | assert encoded == 'QXJnaCwgR3Jvb3Z5IHlvdSBzYXksIG1hdGU/' 6 | 7 | // And decode. 8 | byte[] decoded = encoded.decodeBase64() 9 | assert new String(decoded) == s 10 | -------------------------------------------------------------------------------- /chapters/strings/capitalize.groovy: -------------------------------------------------------------------------------- 1 | // Groovy adds the capitalize() method to the String class. 2 | assert 'mrHaki'.capitalize() == 'MrHaki' 3 | assert 'groovy'.capitalize() == 'Groovy' 4 | assert 'groovy is Gr8!'.capitalize() == 'Groovy is Gr8!' 5 | -------------------------------------------------------------------------------- /chapters/strings/character.groovy: -------------------------------------------------------------------------------- 1 | // We can ask a char if it is a letter, digit, 2 | // lowercase, uppercase or whitespace. 3 | def str = 'a1cB \n.9' 4 | def characters = str.chars // Convert to char[] 5 | 6 | assert characters[0].isLetter() // 'a' 7 | assert characters[1].isDigit() // '1' 8 | assert characters[2].isLowerCase() // 'c' 9 | assert characters[3].isUpperCase() // 'B' 10 | assert characters[4].isWhitespace() // ' ' 11 | assert characters[5].isWhitespace() // '\n' 12 | assert !characters[6].isLetterOrDigit() // '.' 13 | assert characters[7].isLetterOrDigit() // '9' 14 | -------------------------------------------------------------------------------- /chapters/strings/continuation.groovy: -------------------------------------------------------------------------------- 1 | def name ='mrhaki' 2 | 3 | def s = "This is not a multiline\ 4 | String, $name, but the continuation\ 5 | character (\\) makes it more readable." 6 | 7 | assert s == 'This is not a multiline String, mrhaki, but the continuation character (\\) makes it more readable.' 8 | -------------------------------------------------------------------------------- /chapters/strings/convertboolean.groovy: -------------------------------------------------------------------------------- 1 | // Groovy can convert different String values 2 | // to a Boolean. 3 | assert "y".toBoolean() 4 | assert 'TRUE'.toBoolean() 5 | assert ' trUe '.toBoolean() 6 | assert " y".toBoolean() 7 | assert "1".toBoolean() 8 | 9 | assert ! 'other'.toBoolean() 10 | assert ! '0'.toBoolean() 11 | assert ! 'no'.toBoolean() 12 | assert ! ' FalSe'.toBoolean() 13 | -------------------------------------------------------------------------------- /chapters/strings/expand.groovy: -------------------------------------------------------------------------------- 1 | // Simple ruler to display 0 up to 30 2 | def ruler = (0..30).inject('\n') { result, c -> 3 | result += (c % 10) 4 | } 5 | 6 | def stringWithTabs = 'Groovy\tGrails\tGriffon' 7 | 8 | println ruler 9 | println stringWithTabs.expand() // default tab stop is 8 10 | println stringWithTabs.expand(10) // tab stop is 10 11 | 12 | // Output: 13 | // 0123456789012345678901234567890 14 | // Groovy Grails Griffon 15 | // Groovy Grails Griffon 16 | 17 | assert stringWithTabs.expand() == 'Groovy Grails Griffon' 18 | assert stringWithTabs.expand(10) == 'Groovy Grails Griffon' 19 | 20 | 21 | def stringWithSpaces = 'Hubert Klein Ikkink' 22 | def stringWithSpaces10 = 'Hubert Klein Ikkink' 23 | println ruler 24 | println stringWithSpaces 25 | println stringWithSpaces10 26 | 27 | // Output: 28 | // 0123456789012345678901234567890 29 | // Hubert Klein Ikkink 30 | // Hubert Klein Ikkink 31 | 32 | assert stringWithSpaces.unexpand() == 'Hubert\tKlein\tIkkink' 33 | assert stringWithSpaces10.unexpand(10) == 'Hubert\tKlein\tIkkink' 34 | -------------------------------------------------------------------------------- /chapters/strings/gstring.groovy: -------------------------------------------------------------------------------- 1 | // We can do inquiries on a GString object. 2 | def user = 'mrhaki' 3 | def language = 'Groovy' 4 | 5 | def s = "Hello ${user}, welcome to ${language}." 6 | 7 | assert s.valueCount == 2 // Count variable parts. 8 | assert s.values == ['mrhaki', 'Groovy'] // We can see the values. 9 | assert s.getValue(0) == 'mrhaki' 10 | assert s.getValue(1) == 'Groovy' 11 | assert s.length() == 32 12 | assert s.strings[0] == 'Hello ' // We can look at the fixed parts. 13 | assert s.strings[1] == ', welcome to ' 14 | assert s.strings[2] == '.' 15 | assert s == 'Hello mrhaki, welcome to Groovy.' 16 | -------------------------------------------------------------------------------- /chapters/strings/isnumber.groovy: -------------------------------------------------------------------------------- 1 | assert "42".isNumber() 2 | assert '42'.isInteger() && '42'.isLong() && '42'.isBigInteger() 3 | assert '42.42'.isDouble() && /42.42/.isBigDecimal() && '42.42'.isFloat() 4 | -------------------------------------------------------------------------------- /chapters/strings/lines.groovy: -------------------------------------------------------------------------------- 1 | //TODO 2 | def multiline = '''\ 3 | Groovy is closely related to Java, 4 | so it is quite easy to make a transition. 5 | ''' 6 | 7 | // eachLine takes a closure with one argument, that 8 | // contains the complete line. 9 | multiline.eachLine { 10 | if (it =~ /Groovy/) { 11 | println it // Output: Groovy is closely related to Java, 12 | } 13 | } 14 | 15 | // or eachLine has a closure with two argument, the current line 16 | // and the line count. 17 | multiline.eachLine { line, count -> 18 | if (count == 0) { 19 | println "line $count: $line" // Output: line 0: Groovy is closely related to Java, 20 | } 21 | } 22 | 23 | def platformLinefeeds = multiline.denormalize() 24 | def linefeeds = multiline.normalize() 25 | 26 | // Read all lines and convert to list. 27 | def list = multiline.readLines() 28 | assert list instanceof ArrayList 29 | assert 2 == list.size() 30 | assert 'Groovy is closely related to Java,' == list[0] 31 | 32 | def records = """\ 33 | mrhaki\tGroovy 34 | hubert\tJava 35 | """ 36 | 37 | // splitEachLine will split each line with the specified 38 | // separator. The closure has one argument, the list of 39 | // elements separated by the separator. 40 | records.splitEachLine('\t') { items -> 41 | println items[0] + " likes " + items[1] 42 | } 43 | // Output: 44 | // mrhaki likes Groovy 45 | // hubert likes Java 46 | -------------------------------------------------------------------------------- /chapters/strings/multiply.groovy: -------------------------------------------------------------------------------- 1 | // Groovy adds the multiply() method to the String class. 2 | '-'.multiply(80) 3 | '-' * 80 // We can also use the '*' operator. -------------------------------------------------------------------------------- /chapters/strings/normalize.groovy: -------------------------------------------------------------------------------- 1 | def text = 'First line\r\nSecond line\r\n' 2 | def textNormalized = text.normalize() 3 | def LS = System.properties['line.separator'] 4 | 5 | assert textNormalized == 'First line\nSecond line\n' 6 | assert textNormalized.denormalize() == "First line${LS}Second line${LS}" 7 | -------------------------------------------------------------------------------- /chapters/strings/padding.groovy: -------------------------------------------------------------------------------- 1 | assert 'Groovy'.center(12) == ' Groovy ' 2 | assert "Groovy".padRight(12) == 'Groovy ' 3 | assert 'Groovy'.padLeft(12) == ' Groovy' 4 | 5 | assert "Groovy".center(12, '-') == '---Groovy---' 6 | assert "Groovy".padRight(12, ' *') == 'Groovy * * *' 7 | assert 'Groovy'.padLeft(20, 'Groovy ') == 'Groovy Groovy Groovy' 8 | 9 | def createOutput = { 10 | def NEWLINE = System.properties['line.separator'] 11 | def table = [ 12 | // Page, Response time, Size 13 | ['page1.html', 200, 1201], 14 | ['page2.html', 42, 8853], 15 | ['page3.html', 98, 3432], 16 | ['page4.html', 432, 9081] 17 | ] 18 | 19 | def total = { data, index -> 20 | data.inject(0) { result, row -> result += row[index] } 21 | } 22 | def totalTime = total.curry(table, 1) 23 | def totalSize = total.curry(table, 2) 24 | 25 | def out = new StringBuffer() 26 | out << ' Summary '.center(15, "*") << NEWLINE << NEWLINE 27 | out << 'Total pages:'.padRight(25) 28 | out << table.size().toString().padLeft(6) << NEWLINE 29 | out << 'Total response time (ms):'.padRight(25) 30 | out << totalTime().toString().padLeft(6) << NEWLINE 31 | out << 'Total size (KB):'.padRight(25) 32 | out << totalSize().toString().padLeft(6) << NEWLINE << NEWLINE 33 | 34 | out << ' Details '.center(15, "*") << NEWLINE << NEWLINE 35 | table.each { 36 | out << it[0].padRight(14) 37 | out << it[1].toString().padLeft(5) 38 | out << it[2].toString().padLeft(8) 39 | out << '\n' 40 | } 41 | out.toString() 42 | } 43 | 44 | def expectedOutput = """\ 45 | *** Summary *** 46 | 47 | Total pages: 4 48 | Total response time (ms): 772 49 | Total size (KB): 22567 50 | 51 | *** Details *** 52 | 53 | page1.html 200 1201 54 | page2.html 42 8853 55 | page3.html 98 3432 56 | page4.html 432 9081 57 | """ 58 | 59 | assert createOutput() == expectedOutput 60 | -------------------------------------------------------------------------------- /chapters/strings/process.groovy: -------------------------------------------------------------------------------- 1 | // Convert found JPEG files with ImageMagick. 2 | new File('.').eachFileMatch(~/.*.jpg$/) { 3 | // Use ImageMagick convert command to create a thumbnail for a JPEG image. 4 | def converter = "convert ${it.name} -thumbnail 100x100 thumb-${it.name - '.jpg'}.gif".execute() 5 | converter.waitFor() 6 | 7 | if (converter.exitValue()) { 8 | println "Error creating thumbnail for ${it.name}:" 9 | println converter.text 10 | } else { 11 | println "Created a thumbnail for ${it.name}." 12 | } 13 | } 14 | 15 | // We can also use a List. Useful if arguments have spaces or 16 | // for shell wildcards like *. 17 | def thumbnail = ["convert", "file.jpg", "-thumbnail", "100x100", "thumb-file.gif"].execute() 18 | thumbnail.waitFor() 19 | 20 | println "Exit value: ${thumbnail.exitValue()}" 21 | println "Output: ${thumbnail.text}" 22 | -------------------------------------------------------------------------------- /chapters/strings/remove.groovy: -------------------------------------------------------------------------------- 1 | // Groovy adds the minus() method to the String class. 2 | def s = 'Groovy and Strings are fun and versatile.' 3 | 4 | assert s - ' and versatile.' == 'Groovy and Strings are fun' // We can use the '-' operator. 5 | assert s.minus(" and versatile") == 'Groovy and Strings are fun.' 6 | assert s - ~/\b\w{3}\b/ == 'Groovy Strings are fun and versatile.' 7 | -------------------------------------------------------------------------------- /chapters/strings/replaceall.groovy: -------------------------------------------------------------------------------- 1 | def s = "Programming with Groovy is fun!" 2 | 3 | assert s.replaceAll(~/is fun!/, "rocks!") == "Programming with Groovy rocks!" // Groovy extension to String. 4 | assert s.replaceAll("fun!", "awesome.") == "Programming with Groovy is awesome." // java.lang.String.replaceAll. 5 | 6 | // Replace found String with result of closure. 7 | def replaced = s.replaceAll(/fun/) { 8 | def list = ['awesome', 'cool', 'okay'] 9 | list[new Random().nextInt(list.size())] 10 | } 11 | assert [ 12 | "Programming with Groovy is awesome!", 13 | "Programming with Groovy is cool!", 14 | "Programming with Groovy is okay!" 15 | ].contains(replaced) 16 | 17 | // Use closure to replace text and use grouping. 18 | // First closure parameter is complete string and following 19 | // parameters are the groups. 20 | def txt = "Generated on 30-10-2009 with Groovy." 21 | def replacedTxt = txt.replaceAll(/.*(\d{2}-\d{2}-\d{4}).*(Gr.*)./) { all, date, lang -> 22 | def dateObj = Date.parse('dd-MM-yyyy', date) 23 | "The text '$all' was created with $lang on a ${dateObj.format('EEEE')}." 24 | } 25 | assert replacedTxt == "The text 'Generated on 30-10-2009 with Groovy.' was created with Groovy on a Friday." 26 | -------------------------------------------------------------------------------- /chapters/strings/split.groovy: -------------------------------------------------------------------------------- 1 | def s = '''\ 2 | username;language,like 3 | mrhaki,Groovy;yes 4 | ''' 5 | 6 | assert s.split() instanceof String[] 7 | assert s.split() == ['username;language,like', 'mrhaki,Groovy;yes'] // Default split on whitespace. ( \t\n\r\f) 8 | assert s.split(/(;|,|\n)/) == ['username', 'language', 'like', 'mrhaki', 'Groovy', 'yes'] // Split argument is a regular expression. 9 | 10 | def result = [] 11 | s.splitEachLine(",") { 12 | result << it // it is list with result of split on , 13 | } 14 | assert result[0] == ['username;language', 'like'] 15 | assert result[1] == ['mrhaki', 'Groovy;yes'] 16 | 17 | assert s.tokenize() instanceof List 18 | assert s.tokenize() == ['username;language,like', 'mrhaki,Groovy;yes'] // Default tokenize on whitespace. ( \t\n\r\f) 19 | assert s.tokenize("\n;,") == ['username', 'language', 'like', 'mrhaki', 'Groovy', 'yes'] // Argument is a String with all tokens we want to tokenize on. 20 | -------------------------------------------------------------------------------- /chapters/strings/sprintf.groovy: -------------------------------------------------------------------------------- 1 | // With the sprintf() method we can format Object values 2 | // according to the java.util.Formatter format rules. 3 | assert sprintf('%2$s %3$s %1$s', ['cool!', 'Groovy', 'is']) == 'Groovy is cool!' 4 | assert sprintf('%05d', 42) == '00042' 5 | -------------------------------------------------------------------------------- /chapters/strings/stringtypes.groovy: -------------------------------------------------------------------------------- 1 | // Single quotes: 2 | def s1 = 'Yep this is a string, and we can use "double quotes" without escaping them.' 3 | def m1 = '''With three quotes 4 | we can write multi-line strings, without all 5 | the hassle of concatenation.''' 6 | 7 | // Double quotes: 8 | def s2 = "Also a string, and of course we can use 'single quotes' without escaping them." 9 | def m2 = """\ 10 | With three double quotes we can have a multi-line string, 11 | and we don't need to add extra + signs. Very easy for 12 | copy-paste.""" 13 | 14 | // Slashy: 15 | def s3 = /Well almost everyting goes in a "slashy" 'string' without escaping. Good for readable regular expressions!/ 16 | 17 | 18 | // Simple Groovy expressions can be used in a GString: 19 | def user = new Expando(name: 'mrhaki', email: 'mail@email.com') 20 | def gs1 = "Hi, your name is ${user?.name}. If I shout I will call you ${user?.name?.toUpperCase()}!" 21 | assert gs1 == 'Hi, your name is mrhaki. If I shout I will call you MRHAKI!' 22 | 23 | // Works also in triple double quoted multi-line strings: 24 | def mailMessage = """ 25 | Hi ${user?.name}, 26 | 27 | thank you for signing up. 28 | You signed up with the following e-mail address: 29 | 30 | ${user?.email} 31 | 32 | Kind regards, 33 | 34 | the support team. 35 | """ 36 | assert mailMessage == ''' 37 | Hi mrhaki, 38 | 39 | thank you for signing up. 40 | You signed up with the following e-mail address: 41 | 42 | mail@email.com 43 | 44 | Kind regards, 45 | 46 | the support team. 47 | ''' 48 | 49 | // We can use closures in GStrings to do lazy evaluation. 50 | // The closure is evaluated when the toString() method on the 51 | // GString is invoked instead of when the GString is created. 52 | // Closures can have zero or one arguments. With one argument we get a writer 53 | // object we can write to, with zero arguments the toString() is called on the 54 | // closure. 55 | def directEval = "Current name value is ${user.name} and email is ${user.email}." 56 | def lazyEval = "Current name value is ${ -> user.name } and email is ${ out -> out << user.email }." 57 | user.name = 'changed username' 58 | user.email = 'changed email' 59 | assert directEval == 'Current name value is mrhaki and email is mail@email.com.' 60 | assert lazyEval == 'Current name value is changed username and email is changed email.' 61 | -------------------------------------------------------------------------------- /chapters/strings/stripleadingspaces.groovy: -------------------------------------------------------------------------------- 1 | def multi = '''\ 2 | Multiline string 3 | with simple 2 space 4 | indentation.''' 5 | 6 | assert multi.stripIndent() == '''\ 7 | Multiline string 8 | with simple 2 space 9 | indentation.''' 10 | 11 | // We can define the number of characters ourselves as well. 12 | assert multi.stripIndent(8) == '''\ 13 | ine string 14 | imple 2 space 15 | ation.''' 16 | -------------------------------------------------------------------------------- /chapters/strings/stripmargin.groovy: -------------------------------------------------------------------------------- 1 | def s = '''\ 2 | |Groovy 3 | |Grails 4 | |Griffon''' 5 | 6 | assert s.stripMargin() == '''\ 7 | Groovy 8 | Grails 9 | Griffon''' 10 | 11 | def s1 = '''\ 12 | * Gradle 13 | * GPars 14 | * Spock''' 15 | 16 | assert s1.stripMargin("* ") == '''\ 17 | Gradle 18 | GPars 19 | Spock''' 20 | -------------------------------------------------------------------------------- /chapters/strings/subscript.groovy: -------------------------------------------------------------------------------- 1 | def s = 'Accessing Strings in Groovy is easy.' 2 | 3 | assert s[0] == 'A' 4 | assert s.getAt(0) == 'A' 5 | assert s[21..26] == 'Groovy' // We can use ranges. 6 | assert s[s.indexOf('ea')..-1] == 'easy.' 7 | 8 | // We can also use each() method on a String. 9 | s[21..26].each { println "$it-" } // Output: G-r-o-o-v-y- 10 | -------------------------------------------------------------------------------- /chapters/strings/tokenize.groovy: -------------------------------------------------------------------------------- 1 | def s = 'one two three four' 2 | 3 | def resultList = s.tokenize() 4 | assert resultList.class.name == 'java.util.ArrayList' 5 | assert resultList == ['one', 'two', 'three', 'four'] 6 | 7 | def resultArray = s.split() 8 | assert resultArray instanceof String[] 9 | assert resultArray == ['one', 'two', 'three', 'four'] 10 | 11 | def s1 = 'Java:Groovy' 12 | assert s1.tokenize(":") == ['Java', 'Groovy'] 13 | assert s1.tokenize(':' as char) == ['Java', 'Groovy'] 14 | -------------------------------------------------------------------------------- /chapters/strings/translation.groovy: -------------------------------------------------------------------------------- 1 | // Source set and replacement set are equal size. 2 | assert 'I love Groovy'.tr('loeG', '1039') == 'I 10v3 9r00vy' 3 | 4 | // Regular expression style range 5 | assert 'mrhaki'.tr('a-k', 'A-K') == 'mrHAKI' 6 | 7 | // Replacement set is smaller than source set. 8 | assert 'Groovy'.tr('ovy', '8') == 'Gr8888' 9 | -------------------------------------------------------------------------------- /chapters/strings/url.groovy: -------------------------------------------------------------------------------- 1 | // Contents of http://www.mrhaki.com/url.html: 2 | // Simple test document 3 | // for testing URL extensions 4 | // in Groovy. 5 | 6 | def url = "http://www.mrhaki.com/url.html".toURL() 7 | 8 | assert url.text == '''\ 9 | Simple test document 10 | for testing URL extensions 11 | in Groovy. 12 | ''' 13 | 14 | def result = [] 15 | url.eachLine { 16 | if (it =~ /Groovy/) { 17 | result << it 18 | } 19 | } 20 | assert result == ['in Groovy.'] 21 | 22 | url.withReader { reader -> 23 | assert reader.readLine() == 'Simple test document' 24 | } 25 | -------------------------------------------------------------------------------- /chapters/syntax/antbuilder.groovy: -------------------------------------------------------------------------------- 1 | // Create AntBuilder. 2 | def ant = new AntBuilder() 3 | 4 | // Simple echo task. 5 | def world = 'Groovy' 6 | ant.echo("Hello $world") // Output: [echo] Hello Groovy 7 | 8 | // Ant task properties are defined as a map. 9 | def options = [src: 'http://mrhaki.blogspot.com', dest: 'blog.html'] 10 | ant.get(options) 11 | // is the same as 12 | ant.get(src: 'http://mrhaki.blogspot.com', dest: 'blog.html') 13 | 14 | // Nice builder syntax for tasks with for example filesets. 15 | def zipfile = 'test.zip' 16 | def current = '.' 17 | ant.zip(destfile: zipfile) { 18 | fileset(dir: current) { 19 | include(name: '**/*.txt') 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /chapters/syntax/askeyword.groovy: -------------------------------------------------------------------------------- 1 | import java.text.MessageFormat as mf // Alias mf 2 | 3 | // Use alias and use as to convert list to Object[]: 4 | assert mf.format('Simple {0}!', ['message'] as Object[]) == 'Simple message!' 5 | 6 | // Normal Groovy list: 7 | def intList = [1,2,3,4] 8 | assert intList.class.name =='java.util.ArrayList' 9 | assert intList.size() == 4 10 | 11 | // Coerse to int[]: 12 | def intArray = intList as int[] 13 | assert intArray.class.name != 'java.util.ArrayList' 14 | assert intArray.length == 4 // Use int[] property. 15 | 16 | // Use as to create Date object from list: 17 | def date = [109, 8, 6] as Date 18 | assert date[Calendar.YEAR] == 2009 19 | assert date[Calendar.MONTH] == 8 20 | assert date[Calendar.DATE] == 6 21 | 22 | // Use as to treat closure as implementation for 23 | // an interface: 24 | def t = new Thread({ println 'hello' } as Runnable) 25 | t.start() // Output: hello 26 | 27 | // Use as to treat map as implementation for 28 | // an interface: 29 | def t2 = new Thread([run: { println 'hello' }] as Runnable) 30 | t2.start() // Output: hello 31 | -------------------------------------------------------------------------------- /chapters/syntax/astype.groovy: -------------------------------------------------------------------------------- 1 | class Size { 2 | def x, y 3 | 4 | Object asType(Class clazz) { 5 | if (clazz == SquaredSize) { 6 | new SquaredSize(x: x**2, y: y**2) 7 | } 8 | } 9 | } 10 | 11 | class SquaredSize { 12 | def x, y 13 | 14 | String toString() { "x: $x, y: $y" } 15 | } 16 | 17 | def size = new Size(x: 10, y: 5) 18 | def squared = size as SquaredSize // Or size.asType(SquaredSize) 19 | 20 | assert squared.toString() == 'x: 100, y: 25' 21 | assert squared.x == 100 22 | assert squared.y == 25 23 | -------------------------------------------------------------------------------- /chapters/syntax/classhierarchy.groovy: -------------------------------------------------------------------------------- 1 | class Shape { } 2 | class Circle extends Shape { } 3 | class Square extends Shape {} 4 | 5 | // Create Square instance. 6 | def square = new Square() 7 | 8 | assert square in Shape 9 | assert square in Square 10 | assert !(square in Circle) 11 | 12 | [Shape.class, Square.class].each { 13 | assert square in it 14 | } 15 | -------------------------------------------------------------------------------- /chapters/syntax/classinfo.groovy: -------------------------------------------------------------------------------- 1 | // Set the stage with a Simple interface 2 | // and Sample class. 3 | interface Simple {} 4 | class Sample implements Simple { 5 | String info 6 | String displayInfo() { "info from $Sample.name" } 7 | } 8 | 9 | // In Groovy we can omit the getClass() method 10 | // invocation to get the name. 11 | assert Sample.name == 'Sample' 12 | assert Sample.class.name == 'Sample' // With getClass() method. 13 | 14 | // We can see which interfaces are implemented by the class. 15 | def interfaces = Sample.interfaces.name 16 | assert 'Simple' in interfaces 17 | assert 'groovy.lang.GroovyObject' in interfaces 18 | 19 | // Let's see which methods are available. 20 | def methods = Sample.methods.name 21 | assert 'setInfo' in methods 22 | assert 'getInfo' in methods 23 | assert 'displayInfo' in methods 24 | 25 | def s = new Sample() 26 | assert s.displayInfo() == 'info from Sample' 27 | -------------------------------------------------------------------------------- /chapters/syntax/configslurper.groovy: -------------------------------------------------------------------------------- 1 | // Configuration script as String, but can also be URL, file. 2 | def mail = ''' 3 | 4 | // Dot notation. 5 | mail.hostname = 'localhost' 6 | 7 | // Scoped closure notation. 8 | mail { 9 | // Using Groovy constructs. 10 | ['user', 'password'].each { 11 | this."${it}" = 'secret' 12 | } 13 | } 14 | 15 | // Environments section. 16 | environments { 17 | dev { 18 | mail.hostname = 'local' 19 | } 20 | test { 21 | mail.hostname = 'test' 22 | } 23 | prod { 24 | mail.hostname = 'prod' 25 | } 26 | } 27 | ''' 28 | 29 | // Another configuration script. 30 | def app = ''' 31 | app { 32 | version = version() // Use method in script. 33 | } 34 | 35 | // Define method to build version info. 36 | def version() { 37 | "1.0-${releasedate.format('yyyy_MM_dd')}" 38 | } 39 | ''' 40 | 41 | // Read mail configuration script for the prod environment. 42 | def mailConfig = new ConfigSlurper('prod').parse(mail) 43 | 44 | // We can pass information to the configuration with 45 | // the setBinding method. 46 | def appSlurper = new ConfigSlurper() 47 | appSlurper.setBinding([releasedate: new Date(109, 9, 10)]) 48 | def appConfig = appSlurper.parse(app) 49 | 50 | // Both configurations are merged into one. 51 | def config = mailConfig.merge(appConfig) 52 | 53 | assert config.mail.hostname == 'prod' 54 | assert config.mail.user == 'secret' 55 | assert config.mail.password == 'secret' 56 | assert config.app.version == '1.0-2009_10_10' 57 | -------------------------------------------------------------------------------- /chapters/syntax/constructor.groovy: -------------------------------------------------------------------------------- 1 | // Default constructor invocation: 2 | def url1 = new URL('http', 'www.mrhaki.com', 80, '/') 3 | assert url1.protocol == 'http' 4 | assert url1.host == 'www.mrhaki.com' 5 | assert url1.port == 80 6 | assert url1.file == '/' 7 | assert url1.path == '/' 8 | 9 | // Explicit coersion with as keyword: 10 | def url2 = ['http', 'www.mrhaki.com', 80, '/'] as URL 11 | assert url2.protocol == 'http' 12 | assert url2.host == 'www.mrhaki.com' 13 | assert url2.port == 80 14 | assert url2.file == '/' 15 | assert url2.path == '/' 16 | 17 | // Implicit coersion by type of variable: 18 | URL url3 = ['http', 'www.mrhaki.com', 80, '/'] 19 | assert url3.protocol == 'http' 20 | assert url3.host == 'www.mrhaki.com' 21 | assert url3.port == 80 22 | assert url3.file == '/' 23 | assert url3.path == '/' 24 | 25 | 26 | // GroovyBean: Groovy creates a constructor 27 | // that takes a map as parameter. 28 | class Sample { 29 | Integer age 30 | String name 31 | } 32 | 33 | def s1 = new Sample([age: 36, name: 'mrhaki']) 34 | assert s1.age == 36 35 | assert s1.name == 'mrhaki' 36 | 37 | // Explicit coersion with as keyword: 38 | def s2 = [age: 36, name: 'mrhaki'] as Sample 39 | assert s2.age == 36 40 | assert s2.name == 'mrhaki' 41 | 42 | // Implicit coersion (by type of variable): 43 | Sample s3 = [age: 36, name: 'mrhaki'] 44 | assert s3.age == 36 45 | assert s3.name == 'mrhaki' 46 | -------------------------------------------------------------------------------- /chapters/syntax/defaultargument.groovy: -------------------------------------------------------------------------------- 1 | def say(msg = 'Hello', name = 'world') { 2 | "$msg $name!" 3 | } 4 | 5 | // We can invoke 3 signatures: 6 | // say(), say(msg), say(msg, name) 7 | assert say() == 'Hello world!' 8 | // Right most parameter with default value is eliminated first. 9 | assert say('Hi') == 'Hi world!' 10 | assert say('Howdy,', 'mrhaki') == 'Howdy, mrhaki!' 11 | -------------------------------------------------------------------------------- /chapters/syntax/defaultimports.groovy: -------------------------------------------------------------------------------- 1 | // The following imports are imported by default: 2 | //import java.io.* 3 | //import java.lang.* 4 | //import java.math.BigDecimal 5 | //import java.math.BigInteger 6 | //import java.net.* 7 | //import java.util.* 8 | //import groovy.lang.* 9 | //import groovy.util.* 10 | 11 | // Look we don't need to explicitly import classes/packages: 12 | def now = new Date() 13 | def file = new File(".") 14 | def url = new URL('http://mrhaki.blogspot.com') 15 | def list = new ArrayList() 16 | -------------------------------------------------------------------------------- /chapters/syntax/delegate.groovy: -------------------------------------------------------------------------------- 1 | class SimpleEvent { 2 | @Delegate Date when 3 | @Delegate List attendees = [] 4 | int maxAttendees = 0 5 | String description 6 | 7 | boolean add(Object value) { 8 | if (attendees.size() < maxAttendees) { 9 | return attendees.add(value) 10 | } else { 11 | throw new IllegalArgumentException("Maximum of ${maxAttendees} attendees exceeded.") 12 | } 13 | } 14 | 15 | } 16 | 17 | def event = new SimpleEvent(when: new Date() + 7, description: 'Small Groovy seminar', maxAttendees: 2) 18 | 19 | assert event.size() == 0 // Delegate to List.size() 20 | assert event.after(new Date()) // Delegate to Date.after() 21 | assert event.description == 'Small Groovy seminar' 22 | assert event.maxAttendees == 2 23 | 24 | event << 'mrhaki' << 'student1' // Delegate to List.leftShift() 25 | assert event.size() == 2 26 | assert event[0] == 'mrhaki' 27 | 28 | event -= 'student1' // Delegate to List.minus() 29 | assert event.size() == 1 30 | 31 | event = new SimpleEvent(when: new Date() + 7, description: 'Small Groovy seminar', maxAttendees: 2) 32 | event << 'mrhaki' << 'student1' 33 | 34 | try { 35 | event << 'three is a crowd.' 36 | assert false 37 | } catch (IllegalArgumentException e) { 38 | assert e.message == 'Maximum of 2 attendees exceeded.' 39 | } 40 | -------------------------------------------------------------------------------- /chapters/syntax/dynamicmethods.groovy: -------------------------------------------------------------------------------- 1 | class Simple { 2 | def hello(value) { 3 | "Hello $value, how are you?" 4 | } 5 | 6 | def goodbye() { 7 | "Have a nice trip." 8 | } 9 | } 10 | 11 | def simple = new Simple() 12 | def methods = ['hello', 'goodbye'] 13 | 14 | assert simple."${methods[0]}"('mrhaki') == 'Hello mrhaki, how are you?' 15 | assert simple."${methods[1]}"() == 'Have a nice trip.' 16 | -------------------------------------------------------------------------------- /chapters/syntax/elvis.groovy: -------------------------------------------------------------------------------- 1 | def sampleText 2 | 3 | // Normal ternary operator. 4 | def ternaryOutput = (sampleText != null) ? sampleText : 'Hello Groovy!' 5 | assert ternaryOutput == 'Hello Groovy!' 6 | 7 | // The Elvis operator in action. We must read: 'If sampleText is not null assign 8 | // sampleText to elvisOuput, otherwise assign 'Viva Las Vegas!' to elvisOutput. 9 | def elvisOutput = sampleText ?: 'Viva Las Vegas!' 10 | assert elvisOutput == 'Viva Las Vegas!' -------------------------------------------------------------------------------- /chapters/syntax/equality.groovy: -------------------------------------------------------------------------------- 1 | Integer myInt = 42 2 | Integer anotherInt = myInt 3 | Integer newInt = 42 4 | Integer different = 101 5 | 6 | assert myInt == anotherInt // In Java: myInt != null && myInt.equals(anotherInt) 7 | assert myInt.is(anotherInt) // In Java: myInt == anotherInt 8 | 9 | assert myInt == newInt 10 | 11 | assert myInt != different 12 | -------------------------------------------------------------------------------- /chapters/syntax/exception.groovy: -------------------------------------------------------------------------------- 1 | try { 2 | def url = new URL('malformedUrl') 3 | assert false, 'We should never get here because of the exception.' 4 | } catch (MalformedURLException e) { 5 | assert true 6 | assert e in MalformedURLException 7 | } 8 | 9 | // Method throws MalformedURLException, but we don't 10 | // have to define it. Groovy will pass the exception 11 | // on to the calling code. 12 | def createUrl() { 13 | new URL('malformedUrl') 14 | } 15 | 16 | try { 17 | def url1 = createUrl() 18 | assert false, 'We should never get here because of the exception.' 19 | } catch (all) { // Groovy shortcut: we can omit the Exception class 20 | // if we want to catch all Exception and descendant objects. 21 | // In Java we have to write catch (Exception all). 22 | assert true 23 | assert all in MalformedURLException 24 | } 25 | -------------------------------------------------------------------------------- /chapters/syntax/expando.groovy: -------------------------------------------------------------------------------- 1 | def user = new Expando(username: 'mrhaki') 2 | assert user.username == 'mrhaki' 3 | 4 | // Add an extra property. 5 | user.email = 'email@host.com' 6 | assert user.email == 'email@host.com' 7 | 8 | // Assign closure as a method. 9 | // The closure can take parameters. 10 | user.printInfo = { writer -> 11 | writer << "Username: $username" 12 | writer << ", email: $email" 13 | } 14 | 15 | def sw = new StringWriter() 16 | user.printInfo(sw) 17 | assert sw.toString() == 'Username: mrhaki, email: email@host.com' 18 | -------------------------------------------------------------------------------- /chapters/syntax/gpath.groovy: -------------------------------------------------------------------------------- 1 | def xml = ''' 2 | 3 | Groovy 4 | Java 5 | Ruby 6 | 7 | ''' 8 | 9 | def languages = new XmlSlurper().parseText(xml) 10 | // Navigate in XML with GPath. 11 | assert languages.language.size() == 3 12 | assert languages.language.find { it.'@id' = 1 }.text() == 'Groovy' 13 | assert languages.language.collect { it.text() } == ['Groovy', 'Java', 'Ruby'] 14 | assert languages.language.find { it = /Groovy/ }['@id'].toInteger() == 1 15 | 16 | // Navigating with GPath through object graph. 17 | assert BigDecimal.metaClass.methods.name.size() == 76 18 | assert BigDecimal.metaClass.methods.findAll { it.static }.name.unique() == ['valueOf'] 19 | assert BigDecimal.metaClass.methods.name.grep(~/toBigInteger.*/) == ['toBigInteger', 'toBigIntegerExact'] 20 | assert BigDecimal.metaClass.properties.name == ['scale', 'class'] 21 | assert BigDecimal.metaClass.properties.type.canonicalName == ['int', 'java.lang.Class'] 22 | -------------------------------------------------------------------------------- /chapters/syntax/grab.groovy: -------------------------------------------------------------------------------- 1 | // With Groovy we can add annotations to import statements. 2 | @Grab(group='commons-lang', module='commons-lang', version='2.4') 3 | import org.apache.commons.lang.SystemUtils 4 | 5 | assert SystemUtils.isJavaVersionAtLeast(1.4) 6 | -------------------------------------------------------------------------------- /chapters/syntax/grabresolver.groovy: -------------------------------------------------------------------------------- 1 | @GrabResolver(name='custom', root='http://customserver/repo', m2Compatible='true') 2 | @Grab('com.mrhaki:groovy-samples:1.0') 3 | import com.mrhaki.groovy.Sample 4 | 5 | def s = new Sample() 6 | s.justToShowGrabResolver() // Just a sample 7 | -------------------------------------------------------------------------------- /chapters/syntax/grape.groovy: -------------------------------------------------------------------------------- 1 | @Grapes([ 2 | @Grab('org.slf4j:slf4j-simple:1.5.11'), 3 | @Grab('com.h2database:h2:1.3.148'), 4 | @GrabConfig(systemClassLoader = true) 5 | ]) 6 | import org.slf4j.* 7 | import groovy.sql.* 8 | 9 | def logger = LoggerFactory.getLogger('sql') 10 | 11 | logger.info 'Initialize SQL' 12 | 13 | // Sql.newInstance needs to look into the System classloader with 14 | // Class.forName(...) instead of the 15 | // Groovy script classloader for the org.h2.Driver class. 16 | def sql = Sql.newInstance('jdbc:h2:mem:test', 'sa', '', 'org.h2.Driver') 17 | 18 | assert sql 19 | 20 | logger.info "Got a SQL connection: $sql" 21 | 22 | // Logger output: 23 | // 7 [main] INFO sql - Initialize SQL 24 | // 346 [main] INFO sql - Got a SQL connection: 25 | -------------------------------------------------------------------------------- /chapters/syntax/groovybeans.groovy: -------------------------------------------------------------------------------- 1 | //TODO 2 | //class Car { 3 | // int numberOfDoors 4 | // String model 5 | // String brand 6 | // boolean automatic 7 | // double price 8 | // 9 | // String toString() { 10 | // "[Car details => brand: '${brand}', model: '${model}', #doors: '${numberOfDoors}', automatic: '${automatic}', price: '${price}']" 11 | // } 12 | //} 13 | // 14 | //Car ford = new Car(brand: 'Ford', model: 'Focus', numberOfDoors: 4, automatic: false, price: 24995) 15 | //Car toyota = new Car(brand: 'Toyota', model: 'Verso') 16 | //toyota.automatic = true // Yes, this invokes setAutomatic. 17 | //toyota.setPrice(28919) // Normal set invocation. 18 | //toyota.setNumberOfDoors 5 // In Groovy we can omit the parentheses. 19 | //println ford // Output: [Car details => brand: 'Ford', model: 'Focus', #doors: '4', automatic: 'false', price: '24995.0'] 20 | //println toyota // Output: [Car details => brand: 'Toyota', model: 'Verso', #doors: '5', automatic: 'true', price: '28919.0'] 21 | // 22 | // 23 | //class Car { 24 | // int numberOfDoors 25 | // String model 26 | // final String brand // Readonly, public getter is generated, no setter. 27 | // boolean automatic 28 | // double price 29 | // 30 | // // Protected setter, public getter is still generated. 31 | // protected void setModel(model) { 32 | // this.model = modelName 33 | // } 34 | // 35 | // String toString() { 36 | // "[Car details => brand: '${brand}', model: '${model}', #doors: '${numberOfDoors}', automatic: '${automatic}', price: '${price}']" 37 | // } 38 | //} 39 | // 40 | // 41 | //class Car { 42 | // int numberOfDoors 43 | // String model 44 | // String brand 45 | // boolean automatic 46 | // double price 47 | // 48 | // public void setBrand(brand) { 49 | // this.brand = brand + ' (set via setter method)' 50 | // } 51 | // 52 | // String toString() { 53 | // "[Car details => brand: '${brand}', model: '${model}', #doors: '${numberOfDoors}', automatic: '${automatic}', price: '${price}']" 54 | // } 55 | //} 56 | // 57 | //Car ford = new Car(brand: 'Ford', model: 'Focus', numberOfDoors: 4, automatic: false, price: 24995) 58 | //println ford.brand // Output: Ford (set via setter method) 59 | // 60 | //ford.@brand = 'Overrule brand' 61 | //println ford.brand // Output: Overrule brand 62 | //println ford.@brand // Output: Overrule brand 63 | 64 | -------------------------------------------------------------------------------- /chapters/syntax/immutable.groovy: -------------------------------------------------------------------------------- 1 | @Immutable class User { 2 | String username, email 3 | Date created = new Date() 4 | Collection roles 5 | } 6 | 7 | def first = new User(username: 'mrhaki', email: 'email@host.com', roles: ['admin', 'user']) 8 | assert first.username == 'mrhaki' 9 | assert first.email == 'email@host.com' 10 | assert first.roles == ['admin', 'user'] 11 | assert new Date().after(first.created) 12 | try { 13 | // Properties are readonly. 14 | first.username = 'new username' 15 | } catch (ReadOnlyPropertyException e) { 16 | assert e.message == 'Cannot set readonly property: username for class: User' 17 | } 18 | try { 19 | // Collections are wrapped in immutable wrapper classes, so we cannot 20 | // change the contents of the collection. 21 | first.roles << 'new role' 22 | } catch (UnsupportedOperationException e) { 23 | assert true 24 | } 25 | 26 | 27 | def date = new Date(109, 8, 16) 28 | def second = new User('user', 'test@host.com', date, ['user']) 29 | assert second.username == 'user' 30 | assert second.email == 'test@host.com' 31 | assert second.roles == ['user'] 32 | assert second.created.format('yyyy/MM/dd') == '2009/09/16' 33 | assert date == second.created 34 | assert !date.is(second.created) // Date, Clonables and arrays are deep copied. 35 | // toString() implementation is created. 36 | assert second.toString() == 'User(user, test@host.com, Wed Sep 16 00:00:00 CEST 2009, [user])' 37 | 38 | 39 | def third = new User(username: 'user', email: 'test@host.com', created: date, roles: ['user']) 40 | // equals() method is also generated by the annotation 41 | // and is based on the property values. 42 | assert third == second 43 | -------------------------------------------------------------------------------- /chapters/syntax/importalias.groovy: -------------------------------------------------------------------------------- 1 | import static java.net.HttpURLConnection.HTTP_OK // Normal Java static import. 2 | import static java.net.HttpURLConnection.HTTP_OK as okay 3 | import static java.net.HttpURLConnection.setFollowRedirects as redirect 4 | import java.net.HttpURLConnection as http // Non static import. 5 | 6 | redirect false // HttpURLConnection.setFollowRedirects(false) 7 | assert HttpURLConnection.followRedirects == false 8 | 9 | def c = (http) 'http://mrhaki.blogspot.com'.toURL().openConnection() 10 | assert c instanceof HttpURLConnection 11 | 12 | assert okay == c.responseCode 13 | assert HTTP_OK == c.responseCode 14 | -------------------------------------------------------------------------------- /chapters/syntax/indexedproperty.groovy: -------------------------------------------------------------------------------- 1 | import groovy.transform.IndexedProperty 2 | 3 | class Group { 4 | String name 5 | List members = [] 6 | } 7 | 8 | class IndexedGroup { 9 | String name 10 | @IndexedProperty List members = [] 11 | } 12 | 13 | def group = new Group(name: 'Groovy') 14 | group.members[0] = 'mrhaki' 15 | group.members[1] = 'Hubert' 16 | assert group.members.size() == 2 17 | assert group.members == ['mrhaki', 'Hubert'] 18 | 19 | try { 20 | group.setMembers(0, 'hubert') // Not index property 21 | } catch (MissingMethodException e) { 22 | assert e 23 | } 24 | 25 | def indexedGroup = new IndexedGroup(name: 'Grails') 26 | indexedGroup.members[0] = 'mrhaki' 27 | indexedGroup.setMembers 1, 'Hubert' 28 | assert indexedGroup.members.size() == 2 29 | assert indexedGroup.getMembers(0) == 'mrhaki' 30 | assert indexedGroup.members[1] == 'Hubert' 31 | -------------------------------------------------------------------------------- /chapters/syntax/inheritconstructor.groovy: -------------------------------------------------------------------------------- 1 | import groovy.transform.InheritConstructors 2 | 3 | @InheritConstructors 4 | class MyException extends Exception { 5 | } 6 | 7 | def e = new MyException() 8 | def e1 = new MyException('message') // Other constructors are available. 9 | assert e1.message == 'message' 10 | 11 | class Person { 12 | String name 13 | 14 | Person(String name) { 15 | this.name = name 16 | } 17 | } 18 | 19 | @InheritConstructors 20 | class Child extends Person {} 21 | 22 | def child = new Child('Liam') 23 | assert child.name == 'Liam' 24 | -------------------------------------------------------------------------------- /chapters/syntax/inoperator.groovy: -------------------------------------------------------------------------------- 1 | // Conditional context 2 | def list = ['Groovy', 'Java'] 3 | assert 'Groovy' in list 4 | assert !('Scala' in list) 5 | 6 | // Write our own in implementation. 7 | class MyObject { 8 | String value 9 | boolean isCase(ch) { 10 | value.contains(ch) 11 | } 12 | } 13 | def myObj = new MyObject(value: 'Groovy') 14 | assert 'oo' in myObj 15 | assert !('oo' in myObj.value) // isCase for String invokes equals. 16 | assert 'Groovy' in myObj 17 | assert 'Groovy' in myObj.value 18 | assert !('a' in myObj) 19 | 20 | // Iterative context 21 | // Use in in a for loop. 22 | def result = '' 23 | for (lang in list ) { 24 | result += lang 25 | } 26 | assert result == 'GroovyJava' 27 | 28 | // Implement iterator() method so we can use 29 | // the in operator for the class. 30 | class Counter { 31 | Integer maxValue 32 | private Integer counter = 0 33 | Iterator iterator() { 34 | [hasNext: { counter <= maxValue }, 35 | next: { counter++ }] as Iterator 36 | } 37 | } 38 | def counter = new Counter(maxValue: 10) 39 | result = '' 40 | for (c in counter) { 41 | result += c 42 | } 43 | assert result == '012345678910' 44 | -------------------------------------------------------------------------------- /chapters/syntax/inspect.groovy: -------------------------------------------------------------------------------- 1 | def map = [username: 'mrhaki'] 2 | assert map.inspect() == '["username":"mrhaki"]' 3 | 4 | def list = [1, 2, 3, 'Groovy'] 5 | assert list.inspect() == '[1, 2, 3, "Groovy"]' 6 | 7 | def range = 0..10 8 | assert range.inspect() == '0..10' 9 | 10 | def str = 'Inspecting object with Groovy' 11 | assert str.inspect() == '"Inspecting object with Groovy"' 12 | //TODO 13 | def dom = groovy.xml.DOMBuilder.newInstance().parseText('Groovy') 14 | println dom.documentElement.inspect() 15 | // Output: 16 | // 17 | // 18 | // Groovy 19 | // 20 | 21 | -------------------------------------------------------------------------------- /chapters/syntax/keywordmethodname.groovy: -------------------------------------------------------------------------------- 1 | class User { 2 | String username 3 | 4 | String 'switch'() { 5 | username = username.reverse() 6 | } 7 | } 8 | 9 | def u = new User(username: 'mrhaki') 10 | assert u.switch() == 'ikahrm' 11 | -------------------------------------------------------------------------------- /chapters/syntax/lazy.groovy: -------------------------------------------------------------------------------- 1 | class Get { 2 | String url 3 | @Lazy URL urlObj = { url?.toURL() }() // Closures allowed to create object. 4 | // Will use a soft reference. 5 | @Lazy(soft=true) String text = urlObj?.text 6 | } 7 | 8 | def g = new Get(url: 'http://mrhaki.blogspot.com/') 9 | assert g.url == 'http://mrhaki.blogspot.com/' 10 | assert g.dump().contains('text=null') 11 | assert g.dump().contains('urlObj=null') 12 | 13 | // Now we access the urlObj property, so the value 14 | // is calculated and we can access it. 15 | assert g.urlObj 16 | assert g.urlObj.protocol == 'http' 17 | assert g.urlObj.host == 'mrhaki.blogspot.com' 18 | assert g.urlObj.path =='/' 19 | 20 | // Now we access the text property, so the contents 21 | // is fetched from the URL. 22 | assert g.text 23 | assert g.text.contains('Messages from mrhaki') 24 | -------------------------------------------------------------------------------- /chapters/syntax/multipleassignment.groovy: -------------------------------------------------------------------------------- 1 | class Size { 2 | int x, y 3 | 4 | Object getAt(int index) { 5 | index == 0 ? x : y 6 | } 7 | } 8 | 9 | def (int myX, int myY) = new Size(x: 12, y: 30) 10 | assert myX == 12 11 | assert myY == 30 12 | -------------------------------------------------------------------------------- /chapters/syntax/namedparameters.groovy: -------------------------------------------------------------------------------- 1 | class Address { 2 | String street, city 3 | int number 4 | } 5 | 6 | class Person { 7 | String name 8 | Address address 9 | String phoneNumber 10 | 11 | def move(newAddress, newPhoneNumber) { 12 | address.street = newAddress.street 13 | address.number = newAddress['number'] 14 | address.city = newAddress."city" 15 | phoneNumber = newPhoneNumber 16 | } 17 | } 18 | 19 | def a = new Address(street: 'Main St.', number: 1, city: 'Duck City') 20 | def p = new Person(name: 'mrhaki', address: a, phoneNumber: '555-123499102') 21 | 22 | p.move(street: 'High Av.', city: 'New Yark', '555-00920120', number: 42) 23 | // Groovy transforms the method invocation to: 24 | // p.move([street: 'High Av.', number: 42, city: 'New Yark'], '555-00920120') 25 | 26 | assert p.address.street == 'High Av.' 27 | assert p.address.number ==42 28 | assert p.address.city == 'New Yark' 29 | assert p.phoneNumber == '555-00920120' 30 | assert p.name == 'mrhaki' 31 | -------------------------------------------------------------------------------- /chapters/syntax/newify.groovy: -------------------------------------------------------------------------------- 1 | class Author { 2 | String name 3 | List books 4 | } 5 | class Book { 6 | String title 7 | } 8 | 9 | def createKing() { 10 | new Author(name: 'Stephen King', books: [ 11 | new Book(title: 'Carrie'), 12 | new Book(title: 'The Shining'), 13 | new Book(title: 'It') 14 | ]) 15 | } 16 | 17 | assert createKing().books.size() == 3 18 | assert createKing().name == 'Stephen King' 19 | assert createKing().books.getAt(0).title == 'Carrie' 20 | 21 | @Newify 22 | def createKingRuby() { 23 | Author.new(name: 'Stephen King', books: [ 24 | Book.new(title: 'Carrie'), 25 | Book.new(title: 'The Shining'), 26 | Book.new(title: 'It') 27 | ]) 28 | } 29 | 30 | assert createKingRuby().books.size() == 3 31 | assert createKingRuby().name == 'Stephen King' 32 | assert createKingRuby().books.title.join(', ') == 'Carrie, The Shining, It' 33 | 34 | @Newify([Author, Book]) 35 | def createKingPython() { 36 | Author(name: 'Stephen King', books: [ 37 | Book(title: 'Carrie'), 38 | Book(title: 'The Shining'), 39 | Book(title: 'It') 40 | ]) 41 | } 42 | 43 | assert createKingPython().books.size() == 3 44 | assert createKingPython().name == 'Stephen King' 45 | assert createKingPython().books.title.find { it == 'It' } == 'It' 46 | -------------------------------------------------------------------------------- /chapters/syntax/newinstance.groovy: -------------------------------------------------------------------------------- 1 | class Blog { 2 | String name 3 | String subject 4 | 5 | Blog() {} 6 | 7 | Blog(String name, String subject) { 8 | this.name = name 9 | this.subject = subject 10 | } 11 | } 12 | 13 | def blog = Blog.newInstance() 14 | assert !blog.name, 'Name has no value' 15 | assert !blog.subject, 'Subject has no value' 16 | 17 | def blog2 = Blog.newInstance(['mrhaki', 'Groovy'] as Object[]) 18 | assert blog2.name == 'mrhaki' 19 | assert blog2.subject == 'Groovy' 20 | 21 | def blog3 = Blog.newInstance([name:'mrhaki', subject: 'Groovy']) 22 | assert blog3.name == 'mrhaki' 23 | assert blog3.subject == 'Groovy' 24 | -------------------------------------------------------------------------------- /chapters/syntax/optionalreturn.groovy: -------------------------------------------------------------------------------- 1 | def simple() { 2 | "Hello world" 3 | } 4 | assert simple() == 'Hello world' 5 | 6 | def doIt(b) { 7 | if (b) { 8 | "You are true" 9 | } else { 10 | "You are false" 11 | } 12 | } 13 | assert doIt(true) == 'You are true' 14 | assert doIt(false) == 'You are false' 15 | 16 | def tryIt(file) { 17 | try { 18 | new File(file).text 19 | } catch (e) { 20 | "Received exception: ${e.message}" 21 | } finally { 22 | println 'Finally is executed but nothing is returned.' 23 | 'Finally reached' 24 | } 25 | } 26 | assert tryIt('invalidfilename') == 'Received exception: invalidfilename (No such file or directory)' 27 | // Create new file with the name test, so we can read it. 28 | new FileWriter('test').withWriter { it.write('file contents') } 29 | assert tryIt('test') == 'file contents' 30 | -------------------------------------------------------------------------------- /chapters/syntax/paramdefault.groovy: -------------------------------------------------------------------------------- 1 | def say(msg = 'Hello', name = 'world') { 2 | "$msg $name!" 3 | } 4 | 5 | // We can invoke 3 signatures: 6 | // say(), say(msg), say(msg, name) 7 | assert say() == 'Hello world!' 8 | // Right most parameter with default value is eliminated first. 9 | assert say('Hi') == 'Hi world!' 10 | assert say('Howdy,', 'mrhaki') == 'Howdy, mrhaki!' 11 | -------------------------------------------------------------------------------- /chapters/syntax/parenthesis.groovy: -------------------------------------------------------------------------------- 1 | println('Parentheses are optional.') 2 | println 'Parentheses are optional.' 3 | 4 | -------------------------------------------------------------------------------- /chapters/syntax/powerassert.groovy: -------------------------------------------------------------------------------- 1 | def list = [1, 2, 3, 4, 5] 2 | assert list.max() - 2 == 4 3 | 4 | /* 5 | assert list.max() - 2 == 4 6 | | | | | 7 | | 5 3 false 8 | [1, 2, 3, 4, 5] 9 | */ 10 | -------------------------------------------------------------------------------- /chapters/syntax/returnvaluecasting.groovy: -------------------------------------------------------------------------------- 1 | // Return type determines type casting of return value. 2 | String simple() { 3 | 42 4 | } 5 | assert simple().class.name == 'java.lang.String' 6 | assert simple() == '42' 7 | 8 | class User { 9 | String name, email 10 | } 11 | 12 | User createUser() { 13 | [name: 'mrhaki', email: 'mail@host.com'] 14 | } 15 | assert createUser() instanceof User 16 | assert createUser().name == 'mrhaki' 17 | assert createUser().email == 'mail@host.com' 18 | -------------------------------------------------------------------------------- /chapters/syntax/safenavigation.groovy: -------------------------------------------------------------------------------- 1 | class Company { 2 | Address address 3 | String name 4 | } 5 | 6 | class Address { 7 | Street street 8 | String postalCode 9 | String city 10 | } 11 | 12 | class Street { 13 | String name 14 | String number 15 | String additionalInfo 16 | } 17 | 18 | def company = null 19 | 20 | // company can be null. 21 | if (company != null && company.getAddress() != null && company.getAddress().getStreet() != null) { 22 | println company.address.street.name 23 | } 24 | 25 | // company can be null. 26 | println company?.address?.street?.name 27 | -------------------------------------------------------------------------------- /chapters/syntax/semicolons.groovy: -------------------------------------------------------------------------------- 1 | assert true 2 | assert !false 3 | 4 | assert true; assert !false // To separate statements on one line we need a ; 5 | -------------------------------------------------------------------------------- /chapters/syntax/shutdownhook.groovy: -------------------------------------------------------------------------------- 1 | addShutdownHook { 2 | println '' 3 | println 'Script is ended.' 4 | } 5 | 6 | println 'Script is started.' 7 | println 'Press Ctrl+C to stop this script or wait 10 seconds.' 8 | (1..10).each { 9 | print "..$it" 10 | Thread.sleep 1000 11 | } 12 | -------------------------------------------------------------------------------- /chapters/syntax/singleton.groovy: -------------------------------------------------------------------------------- 1 | package com.mrhaki.blog 2 | 3 | // Old style singleton class. 4 | public class StringUtil { 5 | private static final StringUtil instance = new StringUtil(); 6 | 7 | private StringUtil() { 8 | } 9 | 10 | public static StringUtil getInstance() { 11 | return instance; 12 | } 13 | 14 | int count(text) { 15 | text.size() 16 | } 17 | } 18 | 19 | assert StringUtil.instance.count('mrhaki') == 6 20 | 21 | // Use @Singleton to create a valid singleton class. 22 | // We can also use @Singleton(lazy=true) for a lazy loading 23 | // singleton class. 24 | @Singleton 25 | class Util { 26 | int count(text) { 27 | text.size() 28 | } 29 | } 30 | 31 | assert Util.instance.count("mrhaki") == 6 32 | 33 | try { 34 | new Util() 35 | } catch (e) { 36 | assert e instanceof RuntimeException 37 | assert "Can't instantiate singleton com.mrhaki.blog.Util. Use com.mrhaki.blog.Util.instance" == e.message 38 | } 39 | -------------------------------------------------------------------------------- /chapters/syntax/spaceship.groovy: -------------------------------------------------------------------------------- 1 | class Person implements Comparable { 2 | String username 3 | String email 4 | 5 | int compareTo(other) { 6 | this.username <=> other.username 7 | } 8 | } 9 | 10 | assert ('a' <=> 'b') == -1 11 | assert (42 <=> 42) == 0 12 | assert (new Person([username:'mrhaki', email: 'test@email.com']) <=> new Person([username:'zavaria', email:'tester@email.com'])) == -1 13 | assert [4, 2, 1, 3].sort{ a, b -> a <=> b } == [1, 2, 3, 4] 14 | -------------------------------------------------------------------------------- /chapters/syntax/spreaddotoperator.groovy: -------------------------------------------------------------------------------- 1 | class Language { 2 | String lang 3 | def speak() { "$lang speaks." } 4 | } 5 | 6 | // Create a list with 3 objects. Each object has a lang 7 | // property and a speak() method. 8 | def list = [ 9 | new Language(lang: 'Groovy'), 10 | new Language(lang: 'Java'), 11 | new Language(lang: 'Scala') 12 | ] 13 | 14 | // Use the spread-dot operator to invoke the speak() method. 15 | assert list*.speak() == ['Groovy speaks.', 'Java speaks.', 'Scala speaks.'] 16 | assert list.collect{ it.speak() } == ['Groovy speaks.', 'Java speaks.', 'Scala speaks.'] 17 | 18 | // We can also use the spread-dot operator to access 19 | // properties, but we don't need to, because Groovy allows 20 | // direct property access on list members. 21 | assert list*.lang == ['Groovy', 'Java', 'Scala'] 22 | assert list.lang == ['Groovy', 'Java', 'Scala'] 23 | -------------------------------------------------------------------------------- /chapters/syntax/spreadoperator.groovy: -------------------------------------------------------------------------------- 1 | class Simple { 2 | String speak(Integer n, String text, Date date) { 3 | def out = new StringBuffer() 4 | n.times { 5 | out << "Say $text on ${date.format('yyyy-MM-dd')}.\n" 6 | } 7 | out 8 | } 9 | } 10 | 11 | // Spread params list for speak() method. 12 | def params = [ 13 | 2, 14 | "hello world", 15 | new Date().parse("yyyy/MM/dd", "2009/09/01") 16 | ] 17 | assert new Simple().speak(*params) == '''Say hello world on 2009-09-01. 18 | Say hello world on 2009-09-01. 19 | ''' 20 | 21 | // Add a list to another list. 22 | def list = ['Groovy', 'Java'] 23 | assert [*list, 'Scala'] == ['Groovy', 'Java', 'Scala'] 24 | 25 | // Add a range to a list. 26 | def range = 2..5 27 | assert [1, *range, 6] == [1, 2, 3, 4, 5, 6] 28 | 29 | // Add a map to another map. 30 | def map = [name: 'mrhaki', blog: true] 31 | assert [subject: 'Groovy Goodness', *:map] == [name: 'mrhaki', blog: true, subject: 'Groovy Goodness'] 32 | 33 | // Little trick to simulate named parameters for a method. 34 | // It is just a trick so the ordering of the map key/values 35 | // must be the same as the method parameters. 36 | def paramsMap = [ 37 | n: 1, 38 | text: 'hello', 39 | date: new Date().parse("yyyy/MM/dd", "2009/09/04") 40 | ] 41 | def paramsList = paramsMap.values().toList() 42 | assert new Simple().speak(*paramsList) == 'Say hello on 2009-09-04.\n' 43 | -------------------------------------------------------------------------------- /chapters/syntax/switchoperator.groovy: -------------------------------------------------------------------------------- 1 | def testSwitch(val) { 2 | def result 3 | switch (val) { 4 | case ~/^Switch.*Groovy$/: 5 | result = 'Pattern match' 6 | break 7 | case BigInteger: 8 | result = 'Class isInstance' 9 | break 10 | case 60..90: 11 | result = 'Range contains' 12 | break 13 | case [21, 'test', 9.12]: 14 | result = 'List contains' 15 | break 16 | case 42.056: 17 | result = 'Object equals' 18 | break 19 | case { it instanceof Integer && it < 50 }: 20 | result = 'Closure boolean' 21 | break 22 | default: 23 | result = 'Default' 24 | break 25 | } 26 | result 27 | } 28 | 29 | assert testSwitch("Switch to Groovy") == 'Pattern match' 30 | assert testSwitch(42G) == 'Class isInstance' 31 | assert testSwitch(70) == 'Range contains' 32 | assert testSwitch('test') == 'List contains' 33 | assert testSwitch(42.056) == 'Object equals' 34 | assert testSwitch(20) == 'Closure boolean' 35 | assert testSwitch('default') == 'Default' 36 | -------------------------------------------------------------------------------- /chapters/syntax/synchronizedannotation.groovy: -------------------------------------------------------------------------------- 1 | import groovy.transform.Synchronized 2 | 3 | class Util { 4 | private counter = 0 5 | 6 | private def list = ['Groovy'] 7 | 8 | private Object listLock = new Object[0] 9 | 10 | @Synchronized 11 | void workOnCounter() { 12 | assert 0 == counter 13 | counter++ 14 | assert 1 == counter 15 | counter -- 16 | assert 0 == counter 17 | } 18 | 19 | @Synchronized('listLock') 20 | void workOnList() { 21 | assert 'Groovy' == list[0] 22 | list << 'Grails' 23 | assert 2 == list.size() 24 | list = list - 'Grails' 25 | assert 'Groovy' == list[0] 26 | } 27 | } 28 | 29 | def util = new Util() 30 | def tc1 = Thread.start { 31 | 100.times { 32 | util.workOnCounter() 33 | sleep 20 34 | util.workOnList() 35 | sleep 10 36 | } 37 | } 38 | def tc2 = Thread.start { 39 | 100.times { 40 | util.workOnCounter() 41 | sleep 10 42 | util.workOnList() 43 | sleep 15 44 | } 45 | } 46 | tc1.join() 47 | tc2.join() 48 | -------------------------------------------------------------------------------- /chapters/syntax/templateengine.groovy: -------------------------------------------------------------------------------- 1 | import groovy.text.* 2 | 3 | // SimpleTemplateEngine. 4 | def simple = new SimpleTemplateEngine() 5 | def source = '''Dear $name, 6 | Please respond to this e-mail before ${(now + 7).format("dd-MM-yyyy")} 7 | Kind regards, mrhaki''' 8 | def binding = [now: new Date(109, 11, 1), name: 'Hubert Klein Ikkink'] 9 | def output = simple.createTemplate(source).make(binding).toString() 10 | 11 | assert output == "Dear Hubert Klein Ikkink,\nPlease respond to this e-mail before 08-12-2009\nKind regards, mrhaki" 12 | 13 | // GStringTemplateEngine with out variable. 14 | def gstring = new GStringTemplateEngine() 15 | def gsource = '''Dear <%= name %>, 16 | Text is created for <% if (gstring) out << 'GStringTemplateEngine' else out << 'other template engine'%>.''' 17 | def gbinding = [name: 'mrhaki', gstring: true] 18 | def goutput = gstring.createTemplate(gsource).make(gbinding).toString() 19 | 20 | assert goutput == "Dear mrhaki,\nText is created for GStringTemplateEngine." 21 | 22 | // XmlTemplateEngine with gsp:scriplet and gsp:expression tags. 23 | def xmlEngine = new XmlTemplateEngine() 24 | def xml = ''' 25 | 26 | users.each { 27 | it.name 28 | } 29 | ''' 30 | def xmlBinding = [users: [ 31 | new Expando(id: 1, name: 'mrhaki'), 32 | new Expando(id: 2, name: 'Hubert')] 33 | ] 34 | def xmlOutput = xmlEngine.createTemplate(xml).make(xmlBinding).toString() 35 | 36 | assert xmlOutput == '''\ 37 | 38 | 39 | mrhaki 40 | 41 | 42 | Hubert 43 | 44 | 45 | ''' 46 | -------------------------------------------------------------------------------- /chapters/syntax/truth.groovy: -------------------------------------------------------------------------------- 1 | // Booleans: 2 | def b1 = false 3 | def b2 = true 4 | assert !b1 5 | assert b2 6 | 7 | // Strings: 8 | def s1 = '' 9 | def s2 = 'abc' 10 | assert !s1 11 | assert s2 12 | 13 | // Numbers: 14 | def n1 = 0 15 | def n2 = 42 16 | assert !n1 17 | assert n2 18 | 19 | // Lists, iterators: 20 | def l1 = [] 21 | def l2 = [1, 3, 6] 22 | assert !l1 23 | assert l2 24 | def i1 = l1.iterator() 25 | def i2 = l2.iterator() 26 | assert !i1 27 | assert i2 28 | 29 | // Maps: 30 | def m1 = [:] 31 | def m2 = ['key': 'value'] 32 | assert !m1 33 | assert m2 34 | 35 | // Objects: 36 | def o1 37 | def o2 = new Expando(name: 'groovy') 38 | assert !o1 39 | assert o2 40 | 41 | // Matchers: 42 | def ma1 = "groovy" =~ /java/ 43 | def ma2 = "groovy" =~ /groovy/ 44 | assert !ma1 45 | assert ma2 46 | 47 | 48 | 49 | def bytes = [] as byte[] 50 | def ints = [1,2,4] as int[] 51 | def floats = [] as float[] 52 | def booleans = [true, false] as boolean[] 53 | 54 | assert !bytes 55 | assert ints 56 | assert !floats 57 | assert booleans 58 | 59 | 60 | class User { 61 | String username 62 | boolean active 63 | 64 | boolean asBoolean() { 65 | active 66 | } 67 | } 68 | 69 | assert new User(username: 'mrhaki', active: true) 70 | assert !new User(username: 'mrhaki', active: false) 71 | 72 | // We can also add the asBoolean method with metaClass. 73 | String.metaClass.asBoolean = { 74 | delegate == /sure/ 75 | } 76 | assert !'true' 77 | assert 'sure' 78 | -------------------------------------------------------------------------------- /chapters/syntax/with.groovy: -------------------------------------------------------------------------------- 1 | class Sample { 2 | String username 3 | String email 4 | List labels = [] 5 | def speakUp() { "I am $username" } 6 | def addLabel(value) { labels << value } 7 | } 8 | 9 | def sample = new Sample() 10 | sample.with { 11 | username = 'mrhaki' 12 | email = 'email@host.com' 13 | println speakUp() // Output: I am mrhaki 14 | addLabel 'Groovy' 15 | addLabel 'Java' 16 | } 17 | assert sample.labels.size() == 2 18 | assert sample.labels[0] == 'Groovy' 19 | assert sample.labels[1] == 'Java' 20 | assert sample.username == 'mrhaki' 21 | assert sample.email == 'email@host.com' 22 | 23 | def sb = new StringBuilder() 24 | sb.with { 25 | append 'Just another way to add ' 26 | append 'strings to the StringBuilder ' 27 | append 'object.' 28 | } 29 | 30 | assert sb.toString() == 'Just another way to add strings to the StringBuilder object.' 31 | 32 | // Another example as seen at 33 | // http://javajeff.blogspot.com/2008/11/getting-groovy-with-with.html 34 | def cal = Calendar.instance 35 | cal.with { 36 | clear() 37 | set(YEAR, 2009) 38 | set MONTH, SEPTEMBER 39 | set DATE, 4 40 | add DATE, 2 41 | } 42 | assert cal.time.format('MMMM d, yyyy') == 'September 6, 2009' 43 | -------------------------------------------------------------------------------- /chapters/xml/markupbuilder.groovy: -------------------------------------------------------------------------------- 1 | //TODO 2 | 3 | import groovy.xml.* 4 | 5 | def writer = new StringWriter() 6 | def html = new MarkupBuilder(writer) 7 | html.html { 8 | head { 9 | title 'Simple document' 10 | } 11 | body(id: 'main') { 12 | h1 'Building HTML the Groovy Way' 13 | p { 14 | mkp.yield 'Mixing text with ' 15 | strong 'bold' 16 | mkp.yield ' elements.' 17 | } 18 | a href: 'more.html', 'Read more...' 19 | } 20 | } 21 | println writer 22 | 23 | /* 24 | Output: 25 | 26 | 27 | Simple document 28 | 29 | 30 |

Building HTML the Groovy Way

31 |

Mixing text with 32 | bold elements. 33 |

34 | Read more.. 35 | 36 | 37 | */ 38 | 39 | def builder = new StreamingMarkupBuilder() 40 | builder.encoding = 'UTF-8' 41 | def books = builder.bind { 42 | mkp.xmlDeclaration() 43 | namespaces << [meta:'http://meta/book/info'] // Or mkp.declareNamespace('meta':'http://meta/book/info') 44 | books(count: 3) { 45 | book(id: 1) { 46 | title lang:'en', 'Groovy in Action' 47 | meta.isbn '1-932394-84-2' 48 | } 49 | book(id: 2) { 50 | title lang:'en', 'Groovy Programming' 51 | meta.isbn '0123725070' 52 | } 53 | book(id: 3) { 54 | title 'Groovy & Grails' // & is converted to & 55 | comment << 'Not yet available.' // Or mkp.comment('Not yet available') 56 | } 57 | book(id: 4) { 58 | mkp.yieldUnescaped 'Griffon Guide' 59 | } 60 | } 61 | } 62 | 63 | println XmlUtil.serialize(books) 64 | 65 | /* 66 | Output: 67 | 68 | 69 | 70 | Groovy in Action 71 | 1-932394-84-2 72 | 73 | 74 | Groovy Programming 75 | 0123725070 76 | 77 | 78 | Groovy & Grails 79 | 80 | 81 | 82 | Griffon Guide 83 | 84 | 85 | */ 86 | -------------------------------------------------------------------------------- /chapters/xml/streamingmarkupbuilder.groovy: -------------------------------------------------------------------------------- 1 | import groovy.xml.* 2 | 3 | def markupBuilder = new StreamingMarkupBuilder() 4 | def xml = markupBuilder.bind { builder -> 5 | client { 6 | name 'mrhaki' 7 | fullName 'Hubert A. Klein Ikkink' 8 | buildAddress builder, 'Main St.', 42, 'Ducktown' 9 | delivery { 10 | buildAddress builder, 'Main Av.', 101, 'Springfield' 11 | remarks 'During office hours' 12 | } 13 | buildItems builder 14 | } 15 | } 16 | 17 | def buildAddress(builder, streetName, number, city) { 18 | builder.address { 19 | street streetName 20 | houseNumber number 21 | buildCity builder, city 22 | } 23 | } 24 | 25 | def buildCity(builder, city) { 26 | builder.city city 27 | } 28 | 29 | def buildItems(builder) { 30 | builder.items { 31 | ['iPod', 'eBook'].eachWithIndex { product, index -> 32 | item(id: index + 1) { 33 | name "Item $product" 34 | } 35 | } 36 | } 37 | } 38 | 39 | assert XmlUtil.serialize(xml) == ''' 40 | 41 | mrhaki 42 | Hubert A. Klein Ikkink 43 |
44 | Main St. 45 | 42 46 | Ducktown 47 |
48 | 49 |
50 | Main Av. 51 | 101 52 | Springfield 53 |
54 | During office hours 55 |
56 | 57 | 58 | Item iPod 59 | 60 | 61 | Item eBook 62 | 63 | 64 |
65 | ''' 66 | -------------------------------------------------------------------------------- /chapters/xml/typeconversion.groovy: -------------------------------------------------------------------------------- 1 | def xml = ''' 2 | 3 | 4 | 36 5 | mrhaki 6 | Hubert A. Klein Ikkink 7 | http://www.mrhaki.com/ 8 | 9 | 10 | ''' 11 | 12 | def users = new XmlSlurper().parseText(xml) 13 | def mrhaki = users.user[0] 14 | assert mrhaki.@loggedin.toBoolean() 15 | assert mrhaki.age.toInteger() == 36 16 | assert mrhaki.name.toString() == 'mrhaki' 17 | assert mrhaki.homepage.toURL() == new URL('http://www.mrhaki.com/') 18 | -------------------------------------------------------------------------------- /chapters/xml/xmlparser.groovy: -------------------------------------------------------------------------------- 1 | import groovy.xml.* 2 | 3 | def xml = ''' 4 | 5 | 6 | Groovy in Action 7 | 1-932394-84-2 8 | 9 | 10 | Groovy Programming 11 | 0123725070 12 | 13 | 14 | Groovy & Grails 15 | 16 | 17 | 18 | Griffon Guide 19 | 20 | 21 | ''' 22 | 23 | def ns = new Namespace('http://meta/book/info', 'meta') 24 | def books = new XmlParser().parseText(xml) 25 | assert books instanceof Node 26 | assert books.book.size() == 4 27 | assert books.breadthFirst().size() == 11 28 | assert books.book[0].title.text() == 'Groovy in Action' 29 | assert books.book.find { it.'@id' == '2' }.title.text() == 'Groovy Programming' 30 | assert books.book.find { it.attribute('id') == '2' }.title.text() == 'Groovy Programming' 31 | assert books.book.findAll { it.title.text() =~ /Groovy/ }.'@id' == ['1', '2', '3'] 32 | assert books.book[ns.isbn].inject([]) { result, v -> result << v.text() } == ['1-932394-84-2', '0123725070'] 33 | -------------------------------------------------------------------------------- /chapters/xml/xmlslurper.groovy: -------------------------------------------------------------------------------- 1 | import groovy.xml.* 2 | 3 | def xml = ''' 4 | 5 | 6 | Groovy in Action 7 | 1-932394-84-2 8 | 9 | 10 | Groovy Programming 11 | 0123725070 12 | 13 | 14 | Groovy & Grails 15 | 16 | 17 | 18 | Griffon Guide 19 | 20 | 21 | ''' 22 | 23 | def books = new XmlSlurper().parseText(xml).declareNamespace([meta:'http://meta/book/info']) 24 | assert books instanceof groovy.util.slurpersupport.GPathResult 25 | assert books.book.size() == 4 26 | assert books.breadthFirst().size() == 11 27 | assert books.book[0].title == 'Groovy in Action' 28 | assert books.book.find { it.@id == '2' }.title == 'Groovy Programming' 29 | assert books.book.findAll { it.title =~ /Groovy/ }.'@id'.list() == [1, 2, 3] 30 | assert books.book.'meta:isbn'.list() == ['1-932394-84-2', '0123725070'] 31 | -------------------------------------------------------------------------------- /checker/checker.groovy: -------------------------------------------------------------------------------- 1 | dontcheck = ['powerassert', 'padding', 'process', 'grabresolver'] 2 | 3 | ok = [] 4 | fail = [] 5 | norun = [] 6 | 7 | def runScript(scriptFile) { 8 | def message = "Evaluating '$scriptFile'..." 9 | def script = (scriptFile.name =~ /(.*)\.groovy$/)[0][1] 10 | if (script in dontcheck) { 11 | message = "$message NORUN" 12 | norun << message 13 | } else { 14 | def shell = new GroovyShell() 15 | try { 16 | shell.evaluate(scriptFile) 17 | message = "$message OK" 18 | ok << message 19 | } catch (all) { 20 | message = "$message FAIL" 21 | fail << message 22 | } 23 | } 24 | println message 25 | } 26 | 27 | def basedirName = '/Users/mrhaki/Projects/Groovy-Goodness-Notebook/chapters' 28 | def basedir = new File(basedirName) 29 | 30 | basedir.eachDir { dir -> 31 | dir.eachFileMatch(~/.*\.groovy$/) { file -> 32 | runScript file 33 | } 34 | } 35 | 36 | def printList(type, messages) { 37 | println '-' * 80 38 | println "# scripts $type: ${messages.size()}" 39 | println '-' * 80 40 | messages.each { println it } 41 | } 42 | 43 | if (ok) { 44 | printList 'OK', ok 45 | } 46 | 47 | if (norun) { 48 | printList 'NORUN', norun 49 | } 50 | 51 | if (fail) { 52 | printList 'FAIL', fail 53 | } 54 | 55 | -------------------------------------------------------------------------------- /template/A5/A5/5.83x8.26_Back_EN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/A5/A5/5.83x8.26_Back_EN.png -------------------------------------------------------------------------------- /template/A5/A5/5.83x8.26_Front_EN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/A5/A5/5.83x8.26_Front_EN.png -------------------------------------------------------------------------------- /template/A5/A5/a5_template.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/A5/A5/a5_template.doc -------------------------------------------------------------------------------- /template/A5/ReadMe_EN.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/A5/ReadMe_EN.pdf -------------------------------------------------------------------------------- /template/CrownQuarto/Crown Quarto/7.44x9.68_Back_EN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/CrownQuarto/Crown Quarto/7.44x9.68_Back_EN.png -------------------------------------------------------------------------------- /template/CrownQuarto/Crown Quarto/7.44x9.68_Front_EN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/CrownQuarto/Crown Quarto/7.44x9.68_Front_EN.png -------------------------------------------------------------------------------- /template/CrownQuarto/Crown Quarto/crown_template.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/CrownQuarto/Crown Quarto/crown_template.doc -------------------------------------------------------------------------------- /template/CrownQuarto/Crown Quarto/sample.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/CrownQuarto/Crown Quarto/sample.doc -------------------------------------------------------------------------------- /template/CrownQuarto/ReadMe_EN.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/CrownQuarto/ReadMe_EN.pdf -------------------------------------------------------------------------------- /template/Royal/ReadMe_EN.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/Royal/ReadMe_EN.pdf -------------------------------------------------------------------------------- /template/Royal/Royal/6.13x9.21_Back_EN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/Royal/Royal/6.13x9.21_Back_EN.png -------------------------------------------------------------------------------- /template/Royal/Royal/6.13x9.21_Front_EN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/Royal/Royal/6.13x9.21_Front_EN.png -------------------------------------------------------------------------------- /template/Royal/Royal/royal_template.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/Royal/Royal/royal_template.doc -------------------------------------------------------------------------------- /template/USTrade-Paperback/ReadMe_EN.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/USTrade-Paperback/ReadMe_EN.pdf -------------------------------------------------------------------------------- /template/USTrade-Paperback/US Trade - Paperback/6x9_Back_EN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/USTrade-Paperback/US Trade - Paperback/6x9_Back_EN.png -------------------------------------------------------------------------------- /template/USTrade-Paperback/US Trade - Paperback/6x9_Front_EN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/USTrade-Paperback/US Trade - Paperback/6x9_Front_EN.png -------------------------------------------------------------------------------- /template/USTrade-Paperback/US Trade - Paperback/ustrade_template.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrhaki/Groovy-Goodness-Notebook/ba9b85e20aa019a5d475607966790440dda83080/template/USTrade-Paperback/US Trade - Paperback/ustrade_template.doc --------------------------------------------------------------------------------