summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/coderay/helpers/file_type.rb2
-rw-r--r--lib/coderay/scanners/groovy.rb247
-rw-r--r--test/scanners/groovy/pleac.expected.raydebug10953
-rw-r--r--test/scanners/groovy/pleac.in.groovy10953
-rw-r--r--test/scanners/groovy/suite.rb4
5 files changed, 22159 insertions, 0 deletions
diff --git a/lib/coderay/helpers/file_type.rb b/lib/coderay/helpers/file_type.rb
index fbc95ec..3c5979a 100644
--- a/lib/coderay/helpers/file_type.rb
+++ b/lib/coderay/helpers/file_type.rb
@@ -87,6 +87,8 @@ module FileType
'mab' => :ruby,
'cpp' => :c,
'c' => :c,
+ 'gvy' => :groovy,
+ 'groovy' => :groovy,
'h' => :c,
'java' => :java,
'js' => :java_script,
diff --git a/lib/coderay/scanners/groovy.rb b/lib/coderay/scanners/groovy.rb
new file mode 100644
index 0000000..5e76357
--- /dev/null
+++ b/lib/coderay/scanners/groovy.rb
@@ -0,0 +1,247 @@
+module CodeRay
+module Scanners
+
+ load :java
+
+ class Groovy < Java
+
+ include Streamable
+ register_for :groovy
+
+ # TODO: Check this!
+ KEYWORDS = Java::KEYWORDS + %w[
+ def assert as in
+ ]
+ KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[
+ case instanceof new return throw typeof while as assert in
+ ]
+
+ MAGIC_VARIABLES = Java::MAGIC_VARIABLES + %w[ it ]
+ # DIRECTIVES = %w[
+ # abstract extends final implements native private protected public
+ # static strictfp synchronized threadsafe throws transient volatile
+ # ]
+
+ IDENT_KIND = WordList.new(:ident).
+ add(KEYWORDS, :keyword).
+ add(MAGIC_VARIABLES, :local_variable).
+ add(TYPES, :type).
+ add(BuiltinTypes::List, :pre_type).
+ add(DIRECTIVES, :directive)
+
+ ESCAPE = / [bfnrtv$\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
+ UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # no 4-byte unicode chars? U[a-fA-F0-9]{8}
+ REGEXP_ESCAPE = / [bBdDsSwW] /x
+ STRING_CONTENT_PATTERN = {
+ "'" => /[^\\$'\n]+/,
+ '"' => /[^\\$"\n]+/,
+ "'''" => /(?>[^\\$']+|'(?!''))+/,
+ '"""' => /(?>[^\\$"]+|"(?!""))+/,
+ '/' => /[^\\$\/\n]+/,
+ }
+
+ def scan_tokens tokens, options
+
+ state = :initial
+ string_delimiter = nil
+ import_clause = class_name_follows = last_token_dot = after_def = false
+ value_expected = true
+
+ until eos?
+
+ kind = nil
+ match = nil
+
+ case state
+
+ when :initial
+
+ if match = scan(/ \s+ | \\\n /x)
+ tokens << [match, :space]
+ if match.index ?\n
+ import_clause = after_def = false
+ value_expected = true
+ end
+ next
+
+ elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
+ value_expected = true
+ after_def = false
+ kind = :comment
+
+ elsif bol? && scan(/ \#!.* /x)
+ kind = :doctype
+
+ elsif import_clause && scan(/ (?!as) #{IDENT} (?: \. #{IDENT} )* (?: \.\* )? /ox)
+ after_def = value_expected = false
+ kind = :include
+
+ elsif match = scan(/ #{IDENT} | \[\] /ox)
+ kind = IDENT_KIND[match]
+ value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match]
+ if last_token_dot
+ kind = :ident
+ elsif class_name_follows
+ kind = :class
+ class_name_follows = false
+ elsif after_def && check(/\s*[({]/)
+ kind = :method
+ after_def = false
+ elsif kind == :ident && check(/:/)
+ kind = :key
+ else
+ class_name_follows = true if match == 'class' || (import_clause && match == 'as')
+ import_clause = match == 'import'
+ after_def = true if match == 'def'
+ end
+
+ # TODO: ~'...', ~"..." and ~/.../ style regexps
+ elsif scan(/ \.\.<? | \*?\.(?!\d)@? | \.& | \?:? | [,?:(\[] | -[->] | \+\+ |
+ && | \|\| | \*\*=? | ==?~ | [-+*%^~&|<>=!]=? | <<<?=? | >>>?=? /x)
+ value_expected = true
+ after_def = false
+ kind = :operator
+
+ elsif scan(/ [)\]}]+ /x)
+ value_expected = after_def = false
+
+ elsif scan(/;/)
+ import_clause = after_def = false
+ value_expected = true
+ kind = :operator
+
+ elsif scan(/\{/)
+ class_name_follows = after_def = false
+ value_expected = true
+ kind = :operator
+
+ elsif check(/[\d.]/)
+ after_def = value_expected = false
+ if scan(/0[xX][0-9A-Fa-f]+/)
+ kind = :hex
+ elsif scan(/(?>0[0-7]+)(?![89.eEfF])/)
+ kind = :oct
+ elsif scan(/\d+[fFdD]|\d*\.\d+(?:[eE][+-]?\d+)?[fFdD]?|\d+[eE][+-]?\d+[fFdD]?/)
+ kind = :float
+ elsif scan(/\d+[lLgG]?/)
+ kind = :integer
+ end
+
+ elsif match = scan(/'''|"""/)
+ after_def = value_expected = false
+ state = :multiline_string
+ tokens << [:open, :string]
+ string_delimiter = match
+ kind = :delimiter
+
+ elsif match = scan(/["']/)
+ after_def = value_expected = false
+ state = match == '/' ? :regexp : :string
+ tokens << [:open, state]
+ string_delimiter = match
+ kind = :delimiter
+
+ elsif value_expected && (match = scan(/\/(?=\S)/))
+ after_def = value_expected = false
+ tokens << [:open, :regexp]
+ state = :regexp
+ string_delimiter = '/'
+ kind = :delimiter
+
+ elsif scan(/ @ #{IDENT} /ox)
+ after_def = value_expected = false
+ kind = :annotation
+
+ elsif scan(/\//)
+ after_def = false
+ value_expected = true
+ kind = :operator
+
+ else
+ getch
+ kind = :error
+
+ end
+
+ when :string, :regexp, :multiline_string
+ if scan(STRING_CONTENT_PATTERN[string_delimiter])
+ kind = :content
+ elsif match = scan(state == :multiline_string ? /'''|"""/ : /["'\/]/)
+ tokens << [match, :delimiter]
+ if state == :regexp
+ modifiers = scan(/[ix]+/)
+ tokens << [modifiers, :modifier] if modifiers && !modifiers.empty?
+ end
+ state = :string if state == :multiline_string
+ tokens << [:close, state]
+ string_delimiter = nil
+ after_def = value_expected = false
+ state = :initial
+ next
+
+ elsif state == :string && (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox))
+ if string_delimiter == "'" && !(match == "\\\\" || match == "\\'")
+ kind = :content
+ else
+ kind = :char
+ end
+ elsif state == :regexp && scan(/ \\ (?: #{ESCAPE} | #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
+ kind = :char
+
+ elsif match = scan(/ \$ #{IDENT} /mox)
+ tokens << [:open, :inline]
+ tokens << ['$', :inline_delimiter]
+ match = match[1..-1]
+ tokens << [match, IDENT_KIND[match]]
+ tokens << [:close, :inline]
+ next
+ elsif match = scan(/ \$ \{ [^}]* \} /mox)
+ # TODO: recursive inline strings
+ tokens << [:open, :inline]
+ tokens << ['${', :inline_delimiter]
+ tokens << [match[2..-2], :ident]
+ tokens << ['}', :inline_delimiter]
+ tokens << [:close, :inline]
+ next
+
+ elsif scan(/ \\. | \$ /mx)
+ kind = :content
+
+ elsif scan(/ \\ | $ /x)
+ tokens << [:close, :delimiter]
+ kind = :error
+ after_def = value_expected = false
+ state = :initial
+ else
+ raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
+ end
+
+ else
+ raise_inspect 'Unknown state', tokens
+
+ end
+
+ match ||= matched
+ if $DEBUG and not kind
+ raise_inspect 'Error token %p in line %d' %
+ [[match, kind], line], tokens
+ end
+ raise_inspect 'Empty token', tokens unless match
+
+ last_token_dot = match == '.'
+
+ tokens << [match, kind]
+
+ end
+
+ if [:string, :regexp].include? state
+ tokens << [:close, state]
+ end
+
+ tokens
+ end
+
+ end
+
+end
+end
diff --git a/test/scanners/groovy/pleac.expected.raydebug b/test/scanners/groovy/pleac.expected.raydebug
new file mode 100644
index 0000000..d66e50d
--- /dev/null
+++ b/test/scanners/groovy/pleac.expected.raydebug
@@ -0,0 +1,10953 @@
+comment(// -*- groovy -*-)
+comment(// The examples make use of Groovy's built-in assert)
+comment(// command so that the script is self-checking)
+
+comment(// @@PLEAC@@_NAME)
+comment(// @@SKIP@@ Groovy)
+
+comment(// @@PLEAC@@_WEB)
+comment(// @@SKIP@@ http://groovy.codehaus.org)
+
+comment(// @@PLEAC@@_1.0)
+comment(//----------------------------------------------------------------------------------)
+ident(string) operator(=) string<delimiter(')char(\\\\)content(n)delimiter(')> comment(// two characters, \\ and an n)
+keyword(assert) ident(string)operator(.)ident(size)operator(()(\)) operator(==) integer(2)
+ident(string) operator(=) string<delimiter(")char(\\n)delimiter(")> comment(// a "newline" character)
+ident(string) operator(=) string<delimiter(')content(\\n)delimiter(')> comment(// a "newline" character)
+
+ident(string) operator(=) string<delimiter(")content(Jon 'Maddog' Orwant)delimiter(")> comment(// literal single quote inside double quotes)
+ident(string) operator(=) string<delimiter(')content(Jon )char(\\')content(Maddog)char(\\')content( Orwant)delimiter(')> comment(// escaped single quotes)
+
+ident(string) operator(=) string<delimiter(')content(Jon "Maddog" Orwant)delimiter(')> comment(// literal double quotes inside single quotes)
+ident(string) operator(=) string<delimiter(")content(Jon )char(\\")content(Maddog)char(\\")content( Orwant)delimiter(")> comment(// escaped double quotes)
+
+ident(string) operator(=) string<delimiter(''')content(
+This is a multiline string declaration
+using single quotes (you can use double quotes\)
+)delimiter(''')>
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.1)
+comment(//----------------------------------------------------------------------------------)
+comment(// accessing substrings)
+ident(string) operator(=) string<delimiter(')content(hippopotamus)delimiter(')>
+ident(start) operator(=) integer(5)operator(;) ident(end) operator(=) integer(7)operator(;) ident(endplus1) operator(=) integer(8)
+keyword(assert) ident(string)operator(.)ident(substring)operator(()ident(start)operator(,) ident(endplus1)(\)) operator(==) string<delimiter(')content(pot)delimiter(')>
+keyword(assert) ident(string)operator([)ident(start)operator(..)ident(end)(]) operator(==) string<delimiter(')content(pot)delimiter(')>
+
+keyword(assert) ident(string)operator(.)ident(substring)operator(()ident(start)(\)) operator(==) string<delimiter(')content(potamus)delimiter(')>
+keyword(assert) ident(string)operator([)ident(start)operator(..)operator(-)integer(1)(]) operator(==) string<delimiter(')content(potamus)delimiter(')>
+
+comment(// String is immutable but new strings can be created in various ways)
+keyword(assert) ident(string) operator(-) string<delimiter(')content(hippo)delimiter(')> operator(-) string<delimiter(')content(mus)delimiter(')> operator(+) string<delimiter(')content(to)delimiter(')> operator(==) string<delimiter(')content(potato)delimiter(')>
+keyword(assert) ident(string)operator(.)ident(replace)operator(()string<delimiter(')content(ppopotam)delimiter(')>operator(,)string<delimiter(')content(bisc)delimiter(')>(\)) operator(==) string<delimiter(')content(hibiscus)delimiter(')>
+keyword(assert) ident(string)operator(.)ident(substring)operator(()integer(0)operator(,) integer(2)(\)) operator(+) string<delimiter(')content(bisc)delimiter(')> operator(+) ident(string)operator([)operator(-)integer(2)operator(..)operator(-)integer(1)(]) operator(==) string<delimiter(')content(hibiscus)delimiter(')>
+comment(// StringBuffer is mutable)
+ident(sb) operator(=) keyword(new) pre_type(StringBuffer)operator(()ident(string)(\))
+ident(sb)operator([)integer(2)operator(..)operator(-)integer(3)(]) operator(=) string<delimiter(')content(bisc)delimiter(')>
+keyword(assert) ident(sb)operator(.)ident(toString)operator(()(\)) operator(==) string<delimiter(')content(hibiscus)delimiter(')>
+
+comment(// No exact pack/unpack equivalents exist in Groovy. Examples here use a custom)
+comment(// implementation to split an original string into chunks of specified length)
+comment(// the method is a modified version of the Java PLEAC version)
+
+comment(// get a 5-character string, skip 8, then grab 2 5-character strings)
+comment(// skipping the trailing spaces, then grab the rest)
+ident(data) operator(=) string<delimiter(')content(hippopotamus means river horse)delimiter(')>
+keyword(def) ident(fields) operator(=) ident(unpack)operator(()string<delimiter(')content(A5 x8 A5 x1 A5 x1 A*)delimiter(')>operator(,) ident(data)(\))
+keyword(assert) ident(fields) operator(==) operator([)string<delimiter(')content(hippo)delimiter(')>operator(,) string<delimiter(')content(means)delimiter(')>operator(,) string<delimiter(')content(river)delimiter(')>operator(,) string<delimiter(')content(horse)delimiter(')>(])
+
+comment(// On a Java 5 or 6 JVM, Groovy can also make use of Scanners:)
+ident(s) operator(=) keyword(new) pre_type(Scanner)operator(()ident(data)(\))
+ident(s)operator(.)ident(findInLine)operator(()regexp<delimiter(/)content((.{5}\).{8}(.{5}\) (.{5}\) (.*\))delimiter(/)>(\))
+ident(m) operator(=) ident(s)operator(.)ident(match)operator(()(\))
+ident(fields) operator(=) type([])
+operator(()integer(1)operator(..)ident(m)operator(.)ident(groupCount)operator(()(\)\))operator(.)ident(each)operator({) ident(fields) operator(<)operator(<) ident(m)operator(.)ident(group)operator(()local_variable(it)(\)) (})
+keyword(assert) ident(fields) operator(==) operator([)string<delimiter(')content(hippo)delimiter(')>operator(,) string<delimiter(')content(means)delimiter(')>operator(,) string<delimiter(')content(river)delimiter(')>operator(,) string<delimiter(')content(horse)delimiter(')>(])
+
+comment(// another scanner example similar to the javadoc example)
+ident(input) operator(=) string<delimiter(')content(1 fish 2 fish red fish blue fish)delimiter(')>
+ident(s) operator(=) keyword(new) pre_type(Scanner)operator(()ident(input)(\))operator(.)ident(useDelimiter)operator(()regexp<delimiter(/)char(\\s)content(*fish)char(\\s)content(*)delimiter(/)>(\))
+ident(fields) operator(=) type([])
+integer(2)operator(.)ident(times)operator({) ident(fields) operator(<)operator(<) ident(s)operator(.)ident(nextInt)operator(()(\)) (})
+integer(2)operator(.)ident(times)operator({) ident(fields) operator(<)operator(<) ident(s)operator(.)ident(next)operator(()(\)) (})
+keyword(assert) ident(fields) operator(==) operator([)integer(1)operator(,) integer(2)operator(,) string<delimiter(')content(red)delimiter(')>operator(,) string<delimiter(')content(blue)delimiter(')>(])
+
+comment(// split at five characters boundaries)
+pre_type(String)type([]) ident(fivers) operator(=) ident(unpack)operator(()string<delimiter(')content(A5 )delimiter(')> operator(*) operator(()ident(data)operator(.)ident(length)operator(()(\)) operator(/) integer(5)(\))operator(,) ident(data)(\))
+keyword(assert) ident(fivers) operator(==) operator([)string<delimiter(")content(hippo)delimiter(")>operator(,) string<delimiter(")content(potam)delimiter(")>operator(,) string<delimiter(")content(us me)delimiter(")>operator(,) string<delimiter(")content(ans r)delimiter(")>operator(,) string<delimiter(")content(iver )delimiter(")>operator(,) string<delimiter(")content(horse)delimiter(")>(])
+
+comment(// chop string into individual characters)
+keyword(assert) string<delimiter(')content(abcd)delimiter(')> keyword(as) pre_type(String)type([]) operator(==) operator([)string<delimiter(')content(a)delimiter(')>operator(,) string<delimiter(')content(b)delimiter(')>operator(,) string<delimiter(')content(c)delimiter(')>operator(,) string<delimiter(')content(d)delimiter(')>(])
+
+ident(string) operator(=) string<delimiter(")content(This is what you have)delimiter(")>
+comment(// Indexing forwards (left to right\))
+comment(// tens 000000000011111111112)
+comment(// units +012345678901234567890)
+comment(// Indexing backwards (right to left\))
+comment(// tens 221111111111000000000)
+comment(// units 109876543210987654321-)
+
+keyword(assert) ident(string)operator([)integer(0)(]) operator(==) string<delimiter(')content(T)delimiter(')>
+keyword(assert) ident(string)operator([)integer(5)operator(..)integer(6)(]) operator(==) string<delimiter(')content(is)delimiter(')>
+keyword(assert) ident(string)operator([)integer(13)operator(..)operator(-)integer(1)(]) operator(==) string<delimiter(')content(you have)delimiter(')>
+keyword(assert) ident(string)operator([)operator(-)integer(1)(]) operator(==) string<delimiter(')content(e)delimiter(')>
+keyword(assert) ident(string)operator([)operator(-)integer(4)operator(..)operator(-)integer(1)(]) operator(==) string<delimiter(')content(have)delimiter(')>
+keyword(assert) ident(string)operator([)operator(-)integer(8)operator(,) operator(-)integer(7)operator(,) operator(-)integer(6)(]) operator(==) string<delimiter(')content(you)delimiter(')>
+
+ident(data) operator(=) keyword(new) pre_type(StringBuffer)operator(()ident(string)(\))
+ident(data)operator([)integer(5)operator(..)integer(6)(]) operator(=) string<delimiter(")content(wasn't)delimiter(")> operator(;) keyword(assert) ident(data)operator(.)ident(toString)operator(()(\)) operator(==) string<delimiter(")content(This wasn't what you have)delimiter(")>
+ident(data)operator([)operator(-)integer(12)operator(..)operator(-)integer(1)(]) operator(=) string<delimiter(")content(ondrous)delimiter(")> operator(;) keyword(assert) ident(data)operator(.)ident(toString)operator(()(\)) operator(==) string<delimiter(")content(This wasn't wondrous)delimiter(")>
+ident(data)operator([)integer(0)operator(..)integer(0)(]) operator(=) string<delimiter(")delimiter(")> operator(;) keyword(assert) ident(data)operator(.)ident(toString)operator(()(\)) operator(==) string<delimiter(")content(his wasn't wondrous)delimiter(")>
+ident(data)operator([)operator(-)integer(10)operator(..)operator(-)integer(1)(]) operator(=) string<delimiter(")delimiter(")> operator(;) keyword(assert) ident(data)operator(.)ident(toString)operator(()(\)) operator(==) string<delimiter(")content(his wasn')delimiter(")>
+
+ident(string) operator(=) string<delimiter(")content(This wasn't wondrous)delimiter(")>
+comment(// check last ten characters match some pattern)
+keyword(assert) ident(string)operator([)operator(-)integer(10)operator(..)operator(-)integer(1)(]) operator(=~) regexp<delimiter(/)content(^t)char(\\s)content(w.*s)content($)delimiter(/)>
+
+ident(string) operator(=) string<delimiter(')content(This is a test)delimiter(')>
+keyword(assert) ident(string)operator([)integer(0)operator(..)integer(4)(])operator(.)ident(replaceAll)operator(()string<delimiter(')content(is)delimiter(')>operator(,) string<delimiter(')content(at)delimiter(')>(\)) operator(+) ident(string)operator([)integer(5)operator(..)operator(-)integer(1)(]) operator(==) string<delimiter(')content(That is a test)delimiter(')>
+
+comment(// exchange the first and last letters in a string)
+ident(string) operator(=) string<delimiter(')content(make a hat)delimiter(')>
+ident(string) operator(=) ident(string)operator([)operator(-)integer(1)(]) operator(+) ident(string)operator([)integer(1)operator(..)operator(-)integer(2)(]) operator(+) ident(string)operator([)integer(0)(])
+keyword(assert) ident(string) operator(==) string<delimiter(')content(take a ham)delimiter(')>
+
+comment(// extract column with unpack)
+ident(string) operator(=) string<delimiter(')content(To be or not to be)delimiter(')>
+
+comment(// skip 6, grab 6)
+keyword(assert) ident(unpack)operator(()string<delimiter(")content(x6 A6)delimiter(")>operator(,) ident(string)(\)) operator(==) operator([)string<delimiter(')content(or not)delimiter(')>(])
+
+comment(// forward 6, grab 2, backward 5, grab 2)
+keyword(assert) ident(unpack)operator(()string<delimiter(")content(x6 A2 X5 A2)delimiter(")>operator(,) ident(string)(\)) operator(==) operator([)string<delimiter(')content(or)delimiter(')>operator(,) string<delimiter(')content(be)delimiter(')>(])
+
+keyword(assert) ident(cut2fmt)operator(()operator([)integer(8)operator(,) integer(14)operator(,) integer(20)operator(,) integer(26)operator(,) integer(30)(]\)) operator(==) string<delimiter(')content(A7 A6 A6 A6 A4 A*)delimiter(')>
+
+comment(// utility method (derived from Java PLEAC version\))
+keyword(def) method(unpack)operator(()pre_type(String) ident(format)operator(,) pre_type(String) ident(data)(\)) operator({)
+ keyword(def) ident(result) operator(=) type([])
+ type(int) ident(formatOffset) operator(=) integer(0)operator(,) ident(dataOffset) operator(=) integer(0)
+ type(int) ident(minDataOffset) operator(=) integer(0)operator(,) ident(maxDataOffset) operator(=) ident(data)operator(.)ident(size)operator(()(\))
+
+ keyword(new) pre_type(StringTokenizer)operator(()ident(format)(\))operator(.)ident(each)operator({) ident(token) operator(->)
+ type(int) ident(tokenLen) operator(=) ident(token)operator(.)ident(length)operator(()(\))
+
+ comment(// count determination)
+ type(int) ident(count) operator(=) integer(0)
+ keyword(if) operator(()ident(tokenLen) operator(==) integer(1)(\)) ident(count) operator(=) integer(1)
+ keyword(else) keyword(if) operator(()ident(token)operator(.)ident(charAt)operator(()integer(1)(\)) operator(==) string<delimiter(')content(*)delimiter(')>(\)) ident(count) operator(=) operator(-)integer(1)
+ keyword(else) ident(count) operator(=) ident(token)operator([)integer(1)operator(..)operator(-)integer(1)(])operator(.)ident(toInteger)operator(()(\))
+
+ comment(// action determination)
+ type(char) ident(action) operator(=) ident(token)operator(.)ident(charAt)operator(()integer(0)(\))
+ keyword(switch) operator(()ident(action)(\)) operator({)
+ keyword(case) string<delimiter(')content(A)delimiter(')>operator(:)
+ keyword(if) operator(()ident(count) operator(==) operator(-)integer(1)(\)) operator({)
+ ident(start) operator(=) operator([)ident(dataOffset)operator(,) ident(maxDataOffset)(])operator(.)ident(min)operator(()(\))
+ ident(result)operator(.)ident(add)operator(()ident(data)operator([)ident(start)operator(..)operator(-)integer(1)(]\))
+ ident(dataOffset) operator(=) ident(maxDataOffset)
+ (}) keyword(else) operator({)
+ ident(start) operator(=) operator([)ident(dataOffset)operator(,) ident(maxDataOffset)(])operator(.)ident(min)operator(()(\))
+ ident(end) operator(=) operator([)ident(dataOffset) operator(+) ident(count)operator(,) ident(maxDataOffset)(])operator(.)ident(min)operator(()(\))
+ ident(result)operator(.)ident(add)operator(()ident(data)operator([)ident(start)operator(..<)ident(end)(]\))
+ ident(dataOffset) operator(+=) ident(count)
+ (})
+ keyword(break)
+ keyword(case) string<delimiter(')content(x)delimiter(')>operator(:)
+ keyword(if) operator(()ident(count) operator(==) operator(-)integer(1)(\)) ident(dataOffset) operator(=) ident(maxDataOffset)
+ keyword(else) ident(dataOffset) operator(+=) ident(count)
+ keyword(break)
+ keyword(case) string<delimiter(')content(X)delimiter(')>operator(:)
+ keyword(if) operator(()ident(count) operator(==) operator(-)integer(1)(\)) ident(dataOffset) operator(=) ident(minDataOffset)
+ keyword(else) ident(dataOffset) operator(-=) ident(count)
+ keyword(break)
+ keyword(default)operator(:)
+ keyword(throw) keyword(new) pre_type(RuntimeException)operator(()string<delimiter(')content(Unknown action token)delimiter(')>operator(,) ident(formatOffset)(\))
+ (})
+ ident(formatOffset) operator(+=) ident(tokenLen) operator(+) integer(1)
+ (})
+ keyword(return) ident(result) keyword(as) pre_type(String)type([])
+(})
+
+comment(// utility method)
+keyword(def) method(cut2fmt)operator(()ident(positions)(\)) operator({)
+ ident(template) operator(=) string<delimiter(')delimiter(')>
+ ident(lastpos) operator(=) integer(1)
+ keyword(for) operator(()ident(pos) keyword(in) ident(positions)(\)) operator({)
+ ident(template) operator(+=) string<delimiter(')content(A)delimiter(')> operator(+) operator(()ident(pos) operator(-) ident(lastpos)(\)) operator(+) string<delimiter(')content( )delimiter(')>
+ ident(lastpos) operator(=) ident(pos)
+ (})
+ keyword(return) ident(template) operator(+) string<delimiter(')content(A*)delimiter(')>
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// use b if b is true, else c)
+ident(b) operator(=) keyword(false)operator(;) ident(c) operator(=) string<delimiter(')content(cat)delimiter(')>
+keyword(assert) operator(()ident(b) operator(?) ident(b) operator(:) ident(c)(\)) operator(==) string<delimiter(')content(cat)delimiter(')>
+ident(b) operator(=) keyword(true)
+keyword(assert) operator(()ident(b) operator(?) ident(b) operator(:) ident(c)(\))
+comment(// can be simplified to 'b || c' if c is a boolean)
+comment(// strictly speaking, b doesn't have to be a boolean,)
+comment(// e.g. an empty list is coerced to boolean false)
+ident(b) operator(=) type([])
+keyword(assert) operator(()ident(b) operator(?) ident(b) operator(:) ident(c)(\)) operator(==) string<delimiter(')content(cat)delimiter(')>
+
+comment(// set x to y unless x is already true)
+ident(x) operator(=) keyword(false)operator(;) ident(y) operator(=) string<delimiter(')content(dog)delimiter(')>
+keyword(if) operator(()operator(!)ident(x)(\)) ident(x) operator(=) ident(y)
+keyword(assert) ident(x) operator(==) string<delimiter(')content(dog)delimiter(')>
+comment(// can be simplified to 'x ||= y' if y is a boolean)
+comment(// x doesn't need to be a boolean, e.g. a non-empty)
+comment(// string is coerced to boolean true)
+ident(x) operator(=) string<delimiter(')content(cat)delimiter(')>
+keyword(if) operator(()operator(!)ident(x)(\)) ident(x) operator(=) ident(y)
+keyword(assert) ident(x) operator(==) string<delimiter(')content(cat)delimiter(')>
+
+comment(// JVM supplies user name)
+comment(// otherwise could use exec or built-in Ant features for reading environment vars)
+keyword(assert) pre_type(System)operator(.)ident(getProperty)operator(()string<delimiter(')content(user.name)delimiter(')>(\))
+
+comment(// test for nullity then for emptyness)
+keyword(def) method(setDefaultIfNullOrEmpty)operator(()ident(startingPoint)(\)) operator({)
+ operator(()operator(!)ident(startingPoint) operator(||) ident(startingPoint)operator(.)ident(length)operator(()(\)) operator(==) integer(0)(\)) operator(?) string<delimiter(')content(Greenwich)delimiter(')> operator(:) ident(startingPoint)
+(})
+keyword(assert) ident(setDefaultIfNullOrEmpty)operator(()keyword(null)(\)) operator(==) string<delimiter(')content(Greenwich)delimiter(')>
+keyword(assert) ident(setDefaultIfNullOrEmpty)operator(()string<delimiter(')delimiter(')>(\)) operator(==) string<delimiter(')content(Greenwich)delimiter(')>
+keyword(assert) ident(setDefaultIfNullOrEmpty)operator(()string<delimiter(')content(Something else)delimiter(')>(\)) operator(==) string<delimiter(')content(Something else)delimiter(')>
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.3)
+comment(//----------------------------------------------------------------------------------)
+ident(v1) operator(=) string<delimiter(')content(alpha)delimiter(')>operator(;) ident(v2) operator(=) string<delimiter(')content(omega)delimiter(')>
+comment(// this can done with explicit swapping via a temp variable)
+comment(// or in a slightly more interesting way with a closure)
+ident(swap) operator(=) operator({) ident(temp) operator(=) ident(v1)operator(;) ident(v1) operator(=) ident(v2)operator(;) ident(v2) operator(=) ident(temp) (})
+ident(swap)operator(()(\))
+keyword(assert) ident(v1) operator(==) string<delimiter(')content(omega)delimiter(')> operator(&&) ident(v2) operator(==) string<delimiter(')content(alpha)delimiter(')>
+comment(// a more generic swap(\) is also possible using Groovy's metaclass mechanisms)
+comment(// but is not idiomatic of Groovy usage)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// char and int are interchangable, apart from precision difference)
+comment(// char use 16 bits while int use 32, requiring a cast from int to char)
+type(char) ident(ch) operator(=) string<delimiter(')content(e)delimiter(')>
+type(int) ident(num) operator(=) ident(ch) comment(// no problem)
+ident(ch) operator(=) operator(()type(char)(\)) ident(num) comment(// needs an explicit cast)
+
+ident(s1) operator(=) string<delimiter(")content(Number )delimiter(")> operator(+) ident(num) operator(+) string<delimiter(")content( is character )delimiter(")> operator(+) operator(()type(char)(\)) ident(num)
+keyword(assert) ident(s1) operator(==) string<delimiter(')content(Number 101 is character e)delimiter(')>
+ident(s2) operator(=) string<delimiter(")content(Character )delimiter(")> operator(+) ident(ch) operator(+) string<delimiter(")content( is number )delimiter(")> operator(+) operator(()type(int)(\)) ident(ch)
+keyword(assert) ident(s2) operator(==) string<delimiter(')content(Character e is number 101)delimiter(')>
+
+comment(// easy conversion between char arrays, char lists and Strings)
+type(char)type([]) ident(ascii) operator(=) string<delimiter(")content(sample)delimiter(")>operator(.)ident(toCharArray)operator(()(\)) comment(// {115, 97, 109, 112, 108, 101})
+keyword(assert) keyword(new) pre_type(String)operator(()ident(ascii)(\)) operator(==) string<delimiter(")content(sample)delimiter(")>
+keyword(assert) keyword(new) pre_type(String)operator(()operator([)integer(115)operator(,) integer(97)operator(,) integer(109)operator(,) integer(112)operator(,) integer(108)operator(,) integer(101)(]) keyword(as) type(char)type([])(\)) operator(==) string<delimiter(")content(sample)delimiter(")>
+
+comment(// convert 'HAL' to 'IBM' (in increasing order of Grooviness\))
+keyword(assert) string<delimiter(")content(HAL)delimiter(")>operator(.)ident(toCharArray)operator(()(\))operator(.)ident(collect)operator({)keyword(new) pre_type(String)operator(()local_variable(it)operator(+)integer(1) keyword(as) type(char)type([])(\)})operator(.)ident(join)operator(()(\)) operator(==) string<delimiter(')content(IBM)delimiter(')>
+keyword(assert) operator(()string<delimiter(")content(HAL)delimiter(")> keyword(as) pre_type(String)type([])(\))operator(.)ident(collect)operator({)local_variable(it)operator(.)ident(next)operator(()(\)})operator(.)ident(join)operator(()(\)) operator(==) string<delimiter(')content(IBM)delimiter(')>
+keyword(assert) string<delimiter(")content(HAL)delimiter(")>operator(.)ident(replaceAll)operator(()string<delimiter(')content(.)delimiter(')>operator(,) operator({)local_variable(it)operator(.)ident(next)operator(()(\)}\)) operator(==) string<delimiter(')content(IBM)delimiter(')>
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.5)
+comment(//----------------------------------------------------------------------------------)
+ident(string) operator(=) string<delimiter(")content(an apple a day)delimiter(")>
+keyword(assert) ident(string)operator([)integer(3)operator(..)integer(7)(])operator(.)ident(split)operator(()string<delimiter(')delimiter(')>(\))operator([)integer(1)operator(..)integer(5)(]) operator(==) operator([)string<delimiter(')content(a)delimiter(')>operator(,) string<delimiter(')content(p)delimiter(')>operator(,) string<delimiter(')content(p)delimiter(')>operator(,) string<delimiter(')content(l)delimiter(')>operator(,) string<delimiter(')content(e)delimiter(')>(])
+keyword(assert) ident(string)operator(.)ident(split)operator(()string<delimiter(')delimiter(')>(\))operator(.)ident(toList)operator(()(\))operator(.)ident(unique)operator(()(\))operator(.)ident(sort)operator(()(\))operator(.)ident(join)operator(()(\)) operator(==) string<delimiter(')content( adelnpy)delimiter(')>
+
+comment(//----------------------------------------------------------------------------------)
+comment(// CheckSum.groovy: Compute 16-bit checksum of input file)
+comment(// Usage: groovy CheckSum <file>)
+comment(// script:)
+ident(checksum) operator(=) integer(0)
+keyword(new) pre_type(File)operator(()ident(args)operator([)integer(0)(]\))operator(.)ident(eachByte)operator({) ident(checksum) operator(+=) local_variable(it) (})
+ident(checksum) operator(%=) operator(()type(int)(\)) pre_type(Math)operator(.)ident(pow)operator(()integer(2)operator(,) integer(16)(\)) operator(-) integer(1)
+ident(println) ident(checksum)
+comment(//----------------------------------------------------------------------------------)
+comment(// to run on its own source code:)
+comment(//=> % groovy CheckSum CheckSum.groovy)
+comment(//=> 9349)
+comment(//----------------------------------------------------------------------------------)
+comment(// Slowcat.groovy: Emulate a s l o w line printer)
+comment(// Usage: groovy Slowcat <file> <delay_millis_between_each_char>)
+comment(// script:)
+ident(delay) operator(=) ident(args)operator([)integer(1)(])operator(.)ident(toInteger)operator(()(\))
+keyword(new) pre_type(File)operator(()ident(args)operator([)integer(0)(]\))operator(.)ident(eachByte)operator({) ident(print) operator(()operator(()type(char)(\)) local_variable(it)(\))operator(;) pre_type(Thread)operator(.)ident(sleep)operator(()ident(delay)(\)) (})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.6)
+comment(//----------------------------------------------------------------------------------)
+keyword(assert) string<delimiter(')content(string)delimiter(')>operator(.)ident(reverse)operator(()(\)) operator(==) string<delimiter(')content(gnirts)delimiter(')>
+
+ident(string) operator(=) string<delimiter(')content(Yoda said, "can you see this?")delimiter(')>
+ident(revwords) operator(=) ident(string)operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))operator(.)ident(toList)operator(()(\))operator(.)ident(reverse)operator(()(\))operator(.)ident(join)operator(()string<delimiter(')content( )delimiter(')>(\))
+keyword(assert) ident(revwords) operator(==) string<delimiter(')content(this?" see you "can said, Yoda)delimiter(')>
+
+ident(words) operator(=) operator([)string<delimiter(')content(bob)delimiter(')>operator(,) string<delimiter(')content(alpha)delimiter(')>operator(,) string<delimiter(')content(rotator)delimiter(')>operator(,) string<delimiter(')content(omega)delimiter(')>operator(,) string<delimiter(')content(reviver)delimiter(')>(])
+ident(long_palindromes) operator(=) ident(words)operator(.)ident(findAll)operator({) ident(w) operator(->) ident(w) operator(==) ident(w)operator(.)ident(reverse)operator(()(\)) operator(&&) ident(w)operator(.)ident(size)operator(()(\)) operator(>) integer(5) (})
+keyword(assert) ident(long_palindromes) operator(==) operator([)string<delimiter(')content(rotator)delimiter(')>operator(,) string<delimiter(')content(reviver)delimiter(')>(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.7)
+comment(//----------------------------------------------------------------------------------)
+ident(s1) operator(=) string<delimiter(')content(abc)content(\\t)content( def)content(\\t)content(ghi )content(\\n)content(\\t)content(x)delimiter(')>
+ident(s2) operator(=) string<delimiter(')content(abc def ghi )content(\\n)content( x)delimiter(')>
+keyword(def) method(expand)operator(()ident(s)(\)) operator({)
+ ident(s)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(toList)operator(()(\))operator(.)ident(collect)operator({)
+ ident(line) operator(=) local_variable(it)
+ keyword(while) operator(()ident(line)operator(.)ident(contains)operator(()string<delimiter(')content(\\t)delimiter(')>(\)\)) operator({)
+ ident(line) operator(=) ident(line)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(([^)char(\\t)content(]*\)()char(\\t)content(\)(.*\))delimiter(/)>(\))operator({)
+ ident(all)operator(,)ident(pre)operator(,)ident(tab)operator(,)ident(suf) operator(->) ident(pre) operator(+) string<delimiter(')content( )delimiter(')> operator(*) operator(()integer(8) operator(-) ident(pre)operator(.)ident(size)operator(()(\)) operator(%) integer(8)(\)) operator(+) ident(suf)
+ (})
+ (})
+ keyword(return) ident(line)
+ (})operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+(})
+keyword(def) method(unexpand)operator(()ident(s)(\)) operator({)
+ ident(s)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(toList)operator(()(\))operator(.)ident(collect)operator({)
+ ident(line) operator(=) local_variable(it)
+ keyword(for) operator(()ident(i) keyword(in) ident(line)operator(.)ident(size)operator(()(\))operator(-)integer(1)operator(..)integer(1)(\)) operator({)
+ keyword(if) operator(()ident(i) operator(%) integer(8) operator(==) integer(0)(\)) operator({)
+ ident(prefix) operator(=) ident(line)operator([)integer(0)operator(..<)ident(i)(])
+ keyword(if) operator(()ident(prefix)operator(.)ident(trim)operator(()(\))operator(.)ident(size)operator(()(\)) operator(!=) ident(prefix)operator(.)ident(size)operator(()(\)\)) operator({)
+ ident(line) operator(=) ident(prefix)operator(.)ident(trim)operator(()(\)) operator(+) string<delimiter(')content(\\t)delimiter(')> operator(+) ident(line)operator([)ident(i)operator(..)operator(-)integer(1)(])
+ (})
+ (})
+ (})
+ keyword(return) ident(line)
+ (})operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+(})
+keyword(assert) ident(expand)operator(()ident(s1)(\)) operator(==) ident(s2)
+keyword(assert) ident(unexpand)operator(()ident(s2)(\)) operator(==) ident(s1)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.8)
+comment(//----------------------------------------------------------------------------------)
+ident(debt) operator(=) integer(150)
+keyword(assert) string<delimiter(")content(You owe )inline<inline_delimiter($)ident(debt)>content( to me)delimiter(")> operator(==) string<delimiter(')content(You owe 150 to me)delimiter(')>
+
+ident(rows) operator(=) integer(24)operator(;) ident(cols) operator(=) integer(80)
+keyword(assert) string<delimiter(")content(I am )inline<inline_delimiter($)ident(rows)>content( high and )inline<inline_delimiter($)ident(cols)>content( wide)delimiter(")> operator(==) string<delimiter(')content(I am 24 high and 80 wide)delimiter(')>
+
+keyword(assert) string<delimiter(')content(I am 17 years old)delimiter(')>operator(.)ident(replaceAll)operator(()regexp<delimiter(/)char(\\d)content(+)delimiter(/)>operator(,) operator({)integer(2)operator(*)local_variable(it)operator(.)ident(toInteger)operator(()(\)}\)) operator(==) string<delimiter(')content(I am 34 years old)delimiter(')>
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.9)
+comment(//----------------------------------------------------------------------------------)
+keyword(assert) string<delimiter(")content(bo peep)delimiter(")>operator(.)ident(toUpperCase)operator(()(\)) operator(==) string<delimiter(')content(BO PEEP)delimiter(')>
+keyword(assert) string<delimiter(')content(JOHN)delimiter(')>operator(.)ident(toLowerCase)operator(()(\)) operator(==) string<delimiter(')content(john)delimiter(')>
+keyword(def) method(capitalize)operator(()ident(s)(\)) operator({)ident(s)operator([)integer(0)(])operator(.)ident(toUpperCase)operator(()(\)) operator(+) operator(()ident(s)operator(.)ident(size)operator(()(\))operator(<)integer(2) operator(?) string<delimiter(')delimiter(')> operator(:) ident(s)operator([)integer(1)operator(..)operator(-)integer(1)(])operator(?)operator(.)ident(toLowerCase)operator(()(\)\)})
+keyword(assert) ident(capitalize)operator(()string<delimiter(')content(joHn)delimiter(')>(\)) operator(==) string<delimiter(')content(John)delimiter(')>
+
+ident(s) operator(=) string<delimiter(")content(thIS is a loNG liNE)delimiter(")>operator(.)ident(replaceAll)operator(()regexp<delimiter(/)char(\\w)content(+)delimiter(/)>(\))operator({)ident(capitalize)operator(()local_variable(it)(\)})
+keyword(assert) ident(s) operator(==) string<delimiter(')content(This Is A Long Line)delimiter(')>
+
+ident(s1) operator(=) string<delimiter(')content(JOhn)delimiter(')>operator(;) ident(s2) operator(=) string<delimiter(')content(joHN)delimiter(')>
+keyword(assert) ident(s1)operator(.)ident(equalsIgnoreCase)operator(()ident(s2)(\))
+
+directive(private) pre_type(Random) ident(rand)
+keyword(def) method(randomCase)operator(()type(char) ident(ch)(\)) operator({)
+ operator(()ident(rand)operator(.)ident(nextInt)operator(()integer(100)(\)) operator(<) integer(20)(\)) operator(?) pre_type(Character)operator(.)ident(toLowerCase)operator(()ident(ch)(\)) operator(:) ident(ch)
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.10)
+comment(//----------------------------------------------------------------------------------)
+ident(n) operator(=) integer(10)
+keyword(assert) string<delimiter(")content(I have )inline<inline_delimiter(${)ident(n+1)inline_delimiter(})>content( guanacos.)delimiter(")> operator(==) string<delimiter(')content(I have 11 guanacos.)delimiter(')>
+keyword(assert) string<delimiter(")content(I have )delimiter(")> operator(+) operator(()ident(n)operator(+)integer(1)(\)) operator(+) string<delimiter(")content( guanacos.)delimiter(")> operator(==) string<delimiter(')content(I have 11 guanacos.)delimiter(')>
+
+comment(// sending templated email is solved in two parts: templating and sending)
+comment(// Part 1: creating an email template)
+ident(naughty) operator(=) string<delimiter(')content(Mr Bad Credit)delimiter(')>
+keyword(def) method(get_manager_list)operator(()ident(s)(\)) operator({) string<delimiter(')content(The Big Boss)delimiter(')> (})
+ident(msg) operator(=) string<delimiter(""")content(
+To: )inline<inline_delimiter($)ident(naughty)>content(
+From: Your Bank
+Cc: )inline<inline_delimiter(${)ident( get_manager_list(naughty\) )inline_delimiter(})>content(
+Date: )inline<inline_delimiter(${)ident( new Date(\) )inline_delimiter(})>content(
+
+Dear )inline<inline_delimiter($)ident(naughty)>content(,
+
+Today, you bounced check number )inline<inline_delimiter(${)ident( 500 + new Random(\).nextInt(100\) )inline_delimiter(})>content( to us.
+Your account is now closed.
+
+Sincerely,
+the management
+)delimiter(""")>
+ident(expected) operator(=) string<delimiter(''')content(
+To: Mr Bad Credit
+From: Your Bank
+Cc: The Big Boss
+Date: XXX
+
+Dear Mr Bad Credit,
+
+Today, you bounced check number XXX to us.
+Your account is now closed.
+
+Sincerely,
+the management
+)delimiter(''')>
+ident(sanitized) operator(=) ident(msg)operator(.)ident(replaceAll)operator(()string<delimiter(')content((?m\)^Date: (.*\))content($)delimiter(')>operator(,)string<delimiter(')content(Date: XXX)delimiter(')>(\))
+ident(sanitized) operator(=) ident(sanitized)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((?m\)check number ()char(\\d)content(+\) to)delimiter(/)>operator(,)string<delimiter(')content(check number XXX to)delimiter(')>(\))
+keyword(assert) ident(sanitized) operator(==) ident(expected)
+comment(// note: Groovy also has several additional built-in templating facilities)
+comment(// Part 2: sending email)
+comment(// SendMail.groovy: Send email)
+comment(// Usage: groovy SendEmail <msgfile>)
+comment(// script:)
+ident(ant) operator(=) keyword(new) ident(AntBuilder)operator(()(\))
+ident(ant)operator(.)ident(mail)operator(()key(from)operator(:)string<delimiter(')content(manager@grumpybank.com)delimiter(')>operator(,) key(tolist)operator(:)string<delimiter(')content(innocent@poorhouse.com)delimiter(')>operator(,)
+ key(encoding)operator(:)string<delimiter(')content(plain)delimiter(')>operator(,) key(mailhost)operator(:)string<delimiter(')content(mail.someserver.com)delimiter(')>operator(,)
+ key(subject)operator(:)string<delimiter(')content(Friendly Letter)delimiter(')>operator(,) key(message)operator(:)string<delimiter(')content(this is a test message)delimiter(')>(\))
+comment(// Ant has many options for setting encoding, security, attachments, etc., see:)
+comment(// http://ant.apache.org/manual/CoreTasks/mail.html)
+comment(// Groovy could also use the Java Mail Api directly if required)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.11)
+comment(//----------------------------------------------------------------------------------)
+keyword(def) ident(raw) operator(=) string<delimiter(''')content(
+ your text
+ goes here
+)delimiter(''')>
+
+keyword(def) ident(expected) operator(=) string<delimiter(''')content(
+your text
+goes here
+)delimiter(''')>
+
+keyword(assert) ident(raw)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(toList)operator(()(\))operator(.)ident(collect)operator({)
+ local_variable(it)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(^)char(\\s)content(+)delimiter(/)>operator(,)string<delimiter(')delimiter(')>(\))
+(})operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\)) operator(+) string<delimiter(')content(\\n)delimiter(')> operator(==) ident(expected)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.12)
+comment(//----------------------------------------------------------------------------------)
+ident(input) operator(=) string<delimiter(''')content(Folding and splicing is the work of an editor,
+ not a mere collection of silicon
+ and
+ mobile electrons!)delimiter(''')>
+
+ident(expected) operator(=) string<delimiter(''')content(Folding and splicing
+is the work of an
+editor, not a mere
+collection of
+silicon and mobile
+electrons!)delimiter(''')>
+
+keyword(def) method(wrap)operator(()ident(text)operator(,) ident(maxSize)(\)) operator({)
+ ident(all) operator(=) type([])
+ ident(line) operator(=) string<delimiter(')delimiter(')>
+ ident(text)operator(.)ident(eachMatch)operator(()regexp<delimiter(/)char(\\S)content(+)delimiter(/)>(\)) operator({)
+ ident(word) operator(=) local_variable(it)operator([)integer(0)(])
+ keyword(if) operator(()ident(line)operator(.)ident(size)operator(()(\)) operator(+) integer(1) operator(+) ident(word)operator(.)ident(size)operator(()(\)) operator(>) ident(maxSize)(\)) operator({)
+ ident(all) operator(+=) ident(line)
+ ident(line) operator(=) ident(word)
+ (}) keyword(else) operator({)
+ ident(line) operator(+=) operator(()ident(line) operator(==) string<delimiter(')delimiter(')> operator(?) ident(word) operator(:) string<delimiter(')content( )delimiter(')> operator(+) ident(word)(\))
+ (})
+ (})
+ ident(all) operator(+=) ident(line)
+ keyword(return) ident(all)operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+(})
+keyword(assert) ident(wrap)operator(()ident(input)operator(,) integer(20)(\)) operator(==) ident(expected)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.13)
+comment(//----------------------------------------------------------------------------------)
+ident(string) operator(=) regexp<delimiter(/)content(Mom said, "Don't do that.")delimiter(/)>
+comment(// backslash special chars)
+keyword(assert) ident(string)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(['"])delimiter(/)>(\))operator({)regexp<delimiter(/)char(\\\\)delimiter(/)>operator(+)local_variable(it)operator([)integer(0)(]}) operator(==) regexp<delimiter(/)content(Mom said, )char(\\")content(Don)char(\\')content(t do that.)char(\\")delimiter(/)> comment(//')
+comment(// double special chars)
+keyword(assert) ident(string)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(['"])delimiter(/)>(\))operator({)local_variable(it)operator([)integer(0)(])operator(+)local_variable(it)operator([)integer(0)(]}) operator(==) regexp<delimiter(/)content(Mom said, ""Don''t do that."")delimiter(/)> comment(//')
+comment(//backslash quote all non-capital letters)
+keyword(assert) string<delimiter(")content(DIR /?)delimiter(")>operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content([^A-Z])delimiter(/)>(\))operator({)regexp<delimiter(/)char(\\\\)delimiter(/)>operator(+)local_variable(it)operator([)integer(0)(]}) operator(==) regexp<delimiter(/)content(DIR)content(\\ )content(\\/)content(\\?)delimiter(/)>
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.14)
+comment(//----------------------------------------------------------------------------------)
+keyword(assert) string<delimiter(')content( x )delimiter(')>operator(.)ident(trim)operator(()(\)) operator(==) string<delimiter(')content(x)delimiter(')>
+comment(// print what's typed, but surrounded by >< symbols)
+comment(// script:)
+keyword(new) pre_type(BufferedReader)operator(()keyword(new) pre_type(InputStreamReader)operator(()pre_type(System)operator(.)ident(in)(\)\))operator(.)ident(eachLine)operator({)
+ ident(println)operator(()string<delimiter(")content(>)delimiter(")> operator(+) local_variable(it)operator(.)ident(trim)operator(()(\)) operator(+) string<delimiter(")content(<)delimiter(")>(\))operator(;)
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.15)
+comment(//----------------------------------------------------------------------------------)
+ident(pattern) operator(=) regexp<delimiter(/)content("([^)char(\\")char(\\\\)content(]*(?:)char(\\\\)content(.[^)char(\\")char(\\\\)content(]*\)*\)",?|([^,]+\),?|,)delimiter(/)>
+ident(line) operator(=) regexp<delimiter(/)content(XYZZY,"","O'Reilly, Inc","Wall, Larry","a )char(\\")content(glug)char(\\")content( bit,",5,"Error, Core Dumped")delimiter(/)>
+ident(m) operator(=) ident(line) operator(=~) ident(pattern)
+ident(expected) operator(=) operator([)regexp<delimiter(/)content(XYZZY)delimiter(/)>operator(,) string<delimiter(')delimiter(')>operator(,) regexp<delimiter(/)content(O'Reilly, Inc)delimiter(/)>operator(,) regexp<delimiter(/)content(Wall, Larry)delimiter(/)>operator(,) comment(//')
+ regexp<delimiter(/)content(a )char(\\")content(glug)char(\\")content( bit,)delimiter(/)>operator(,) regexp<delimiter(/)content(5)delimiter(/)>operator(,) regexp<delimiter(/)content(Error, Core Dumped)delimiter(/)>(])
+keyword(for) operator(()ident(i) keyword(in) integer(0)operator(..<)ident(m)operator(.)ident(size)operator(()(\))operator(.)ident(toInteger)operator(()(\)\))
+ keyword(assert) ident(expected)operator([)ident(i)(]) operator(==) operator(()ident(m)operator([)ident(i)(])operator([)integer(2)(]) operator(?) ident(m)operator([)ident(i)(])operator([)integer(2)(]) operator(:) ident(m)operator([)ident(i)(])operator([)integer(1)(]\))
+
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.16)
+comment(//----------------------------------------------------------------------------------)
+comment(// A quick google search found several Java implementations.)
+comment(// As an example, how to use commons codec is shown below.)
+comment(// Just place the respective jar in your classpath.)
+comment(// Further details: http://jakarta.apache.org/commons/codec)
+comment(// require(groupId:'commons-codec', artifactId:'commons-codec', version:'1.3'\))
+ident(soundex) operator(=) keyword(new) ident(org)operator(.)ident(apache)operator(.)ident(commons)operator(.)ident(codec)operator(.)ident(language)operator(.)ident(Soundex)operator(()(\))
+keyword(assert) ident(soundex)operator(.)ident(soundex)operator(()string<delimiter(')content(Smith)delimiter(')>(\)) operator(==) ident(soundex)operator(.)ident(soundex)operator(()string<delimiter(')content(Smyth)delimiter(')>(\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.17)
+comment(//----------------------------------------------------------------------------------)
+ident(input) operator(=) string<delimiter(''')content(I have analysed the new part. As long as you
+aren't worried about the colour, it is a dropin replacement.)delimiter(''')> comment(//')
+
+ident(expected) operator(=) string<delimiter(''')content(I have analyzed the new part. As long as you
+aren't worried about the color, it is a drop-in replacement.)delimiter(''')> comment(//')
+
+ident(translations) operator(=) operator([)key(colour)operator(:)string<delimiter(')content(color)delimiter(')>operator(,) key(analysed)operator(:)string<delimiter(')content(analyzed)delimiter(')>operator(,) key(dropin)operator(:)string<delimiter(')content(drop-in)delimiter(')>(])
+
+keyword(def) method(fixstyle)operator(()ident(s)(\)) operator({)
+ ident(s)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(toList)operator(()(\))operator(.)ident(collect)operator({)
+ ident(line) operator(=) local_variable(it)
+ ident(translations)operator(.)ident(each)operator({) ident(key)operator(,) ident(value) operator(->)
+ ident(line) operator(=) ident(line)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((?<=)char(\\W)content(\))delimiter(/)> operator(+) ident(key) operator(+) regexp<delimiter(/)content((?=)char(\\W)content(\))delimiter(/)>operator(,) ident(value)(\))
+ (})
+ keyword(return) ident(line)
+ (})operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+(})
+keyword(assert) ident(fixstyle)operator(()ident(input)(\)) operator(==) ident(expected)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_1.18)
+comment(//----------------------------------------------------------------------------------)
+comment(// Solved in two parts: 'screenscrape' text stream and return stream from process)
+comment(// Part 1: text scraping)
+ident(input) operator(=) string<delimiter(''')content(
+ PID PPID PGID WINPID TTY UID STIME COMMAND
+ 4636 1 4636 4636 con 1005 08:24:50 /usr/bin/bash
+ 676 4636 676 788 con 1005 13:53:32 /usr/bin/ps
+)delimiter(''')>
+ident(select1) operator(=) string<delimiter(''')content(
+ PID PPID PGID WINPID TTY UID STIME COMMAND
+ 676 4636 676 788 con 1005 13:53:32 /usr/bin/ps
+)delimiter(''')>
+ident(select2) operator(=) string<delimiter(''')content(
+ PID PPID PGID WINPID TTY UID STIME COMMAND
+ 4636 1 4636 4636 con 1005 08:24:50 /usr/bin/bash
+)delimiter(''')>
+
+comment(// line below must be configured for your unix - this one's cygwin)
+ident(format) operator(=) ident(cut2fmt)operator(()operator([)integer(10)operator(,) integer(18)operator(,) integer(26)operator(,) integer(37)operator(,) integer(42)operator(,) integer(47)operator(,) integer(56)(]\))
+keyword(def) method(psgrep)operator(()ident(s)(\)) operator({)
+ ident(out) operator(=) type([])
+ ident(lines) operator(=) ident(input)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(findAll)operator({) local_variable(it)operator(.)ident(size)operator(()(\)) (})
+ ident(vars) operator(=) ident(unpack)operator(()ident(format)operator(,) ident(lines)operator([)integer(0)(]\))operator(.)ident(toList)operator(()(\))operator(.)ident(collect)operator({) local_variable(it)operator(.)ident(toLowerCase)operator(()(\))operator(.)ident(trim)operator(()(\)) (})
+ ident(out) operator(+=) ident(lines)operator([)integer(0)(])
+ ident(lines)operator([)integer(1)operator(..)operator(-)integer(1)(])operator(.)ident(each)operator({)
+ ident(values) operator(=) ident(unpack)operator(()ident(format)operator(,) local_variable(it)(\))operator(.)ident(toList)operator(()(\))operator(.)ident(collect)operator({)
+ keyword(try) operator({)
+ keyword(return) local_variable(it)operator(.)ident(toInteger)operator(()(\))
+ (}) keyword(catch)operator(()pre_type(NumberFormatException) ident(e)(\)) operator({)
+ keyword(return) local_variable(it)operator(.)ident(trim)operator(()(\))
+ (})
+ (})
+ ident(vars)operator(.)ident(eachWithIndex)operator({) ident(var)operator(,) ident(i) operator(->)
+ ident(binding)operator(.)ident(setVariable)operator(()ident(var)operator(,) ident(values)operator([)ident(i)(]\))
+ (})
+ keyword(if) operator(()keyword(new) ident(GroovyShell)operator(()ident(binding)(\))operator(.)ident(evaluate)operator(()ident(s)(\)\)) ident(out) operator(+=) local_variable(it)
+ (})
+ keyword(return) string<delimiter(')content(\\n)delimiter(')> operator(+) ident(out)operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\)) operator(+) string<delimiter(')content(\\n)delimiter(')>
+(})
+keyword(assert) ident(psgrep)operator(()string<delimiter(')content(winpid < 800)delimiter(')>(\)) operator(==) ident(select1)
+keyword(assert) ident(psgrep)operator(()string<delimiter(')content(uid % 5 == 0 && command =~ /sh)content($)content(/)delimiter(')>(\)) operator(==) ident(select2)
+comment(// Part 2: obtaining text stream from process)
+comment(// unixScript:)
+ident(input) operator(=) string<delimiter(')content(ps)delimiter(')>operator(.)ident(execute)operator(()(\))operator(.)ident(text)
+comment(// cygwinScript:)
+ident(input) operator(=) string<delimiter(')content(path_to_cygwin/bin/ps.exe)delimiter(')>operator(.)ident(execute)operator(()(\))operator(.)ident(text)
+comment(// windowsScript:)
+comment(// can use something like sysinternal.com s pslist (with minor script tweaks\))
+ident(input) operator(=) string<delimiter(')content(pslist.exe)delimiter(')>operator(.)ident(execute)operator(()(\))operator(.)ident(text)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.1)
+comment(//----------------------------------------------------------------------------------)
+comment(// four approaches possible (shown for Integers, similar for floats, double etc.\):)
+comment(// (1\) NumberFormat.getInstance(\).parse(s\) // getInstance(\) can take locale)
+comment(// (2\) Integer.parseInt(s\))
+comment(// (3\) new Integer(s\))
+comment(// (4\) regex)
+keyword(import) include(java.text.*)
+type(int) ident(nb) operator(=) integer(0)
+keyword(try) operator({)
+ ident(nb) operator(=) pre_type(NumberFormat)operator(.)ident(getInstance)operator(()(\))operator(.)ident(parse)operator(()string<delimiter(')content(33.5)delimiter(')>(\)) comment(// '.5' will be ignored)
+ ident(nb) operator(=) pre_type(NumberFormat)operator(.)ident(getInstance)operator(()(\))operator(.)ident(parse)operator(()string<delimiter(')content(abc)delimiter(')>(\))
+(}) keyword(catch) operator(()pre_type(ParseException) ident(ex)(\)) operator({)
+ keyword(assert) ident(ex)operator(.)ident(getMessage)operator(()(\))operator(.)ident(contains)operator(()string<delimiter(')content(abc)delimiter(')>(\))
+(})
+keyword(assert) ident(nb) operator(==) integer(33)
+
+keyword(try) operator({)
+ ident(nb) operator(=) pre_type(Integer)operator(.)ident(parseInt)operator(()string<delimiter(')content(34)delimiter(')>(\))
+ keyword(assert) ident(nb) operator(==) integer(34)
+ ident(nb) operator(=) keyword(new) pre_type(Integer)operator(()string<delimiter(')content(35)delimiter(')>(\))
+ ident(nb) operator(=) pre_type(Integer)operator(.)ident(parseInt)operator(()string<delimiter(')content(abc)delimiter(')>(\))
+(}) keyword(catch) operator(()pre_type(NumberFormatException) ident(ex)(\)) operator({)
+ keyword(assert) ident(ex)operator(.)ident(getMessage)operator(()(\))operator(.)ident(contains)operator(()string<delimiter(')content(abc)delimiter(')>(\))
+(})
+keyword(assert) ident(nb) operator(==) integer(35)
+
+ident(integerPattern) operator(=) regexp<delimiter(/)content(^[+-]?)char(\\d)content(+)content($)delimiter(/)>
+keyword(assert) string<delimiter(')content(-36)delimiter(')> operator(=~) ident(integerPattern)
+keyword(assert) operator(!)operator(()string<delimiter(')content(abc)delimiter(')> operator(=~) ident(integerPattern)(\))
+ident(decimalPattern) operator(=) regexp<delimiter(/)content(^-?(?:)char(\\d)content(+(?:)content(\\.)char(\\d)content(*\)?|)content(\\.)char(\\d)content(+\))content($)delimiter(/)>
+keyword(assert) string<delimiter(')content(37.5)delimiter(')> operator(=~) ident(decimalPattern)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy defaults to BigDecimal if you don't use an explicit float or double)
+ident(wage) operator(=) float(5.36)
+ident(week) operator(=) integer(40) operator(*) ident(wage)
+keyword(assert) string<delimiter(")content(One week's wage is: )char(\\$)inline<inline_delimiter($)ident(week)>delimiter(")> operator(==) regexp<delimiter(/)content(One week's wage is: )content($)content(214.40)delimiter(/)>
+comment(// if you want to use explicit doubles and floats you can still use)
+comment(// printf in version 5, 6 or 7 JVMs)
+comment(// printf('%5.2f', week as double\))
+comment(// => 214.40)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.3)
+comment(//----------------------------------------------------------------------------------)
+ident(a) operator(=) float(0.255)
+ident(b) operator(=) ident(a)operator(.)ident(setScale)operator(()integer(2)operator(,) pre_type(BigDecimal)operator(.)ident(ROUND_HALF_UP)(\))operator(;)
+keyword(assert) ident(a)operator(.)ident(toString)operator(()(\)) operator(==) string<delimiter(')content(0.255)delimiter(')>
+keyword(assert) ident(b)operator(.)ident(toString)operator(()(\)) operator(==) string<delimiter(')content(0.26)delimiter(')>
+
+ident(a) operator(=) operator([)float(3.3) operator(,) float(3.5) operator(,) float(3.7)operator(,) operator(-)float(3.3)(]) keyword(as) type(double)type([])
+comment(// warning rint(\) rounds to nearest integer - slightly different to Perl's int(\))
+ident(rintExpected) operator(=) operator([)float(3.0)operator(,) float(4.0)operator(,) float(4.0)operator(,) operator(-)float(3.0)(]) keyword(as) type(double)type([])
+ident(floorExpected) operator(=) operator([)float(3.0)operator(,) float(3.0)operator(,) float(3.0)operator(,) operator(-)float(4.0)(]) keyword(as) type(double)type([])
+ident(ceilExpected) operator(=) operator([)float(4.0)operator(,) float(4.0)operator(,) float(4.0)operator(,) operator(-)float(3.0)(]) keyword(as) type(double)type([])
+ident(a)operator(.)ident(eachWithIndex)operator({) ident(val)operator(,) ident(i) operator(->)
+ keyword(assert) pre_type(Math)operator(.)ident(rint)operator(()ident(val)(\)) operator(==) ident(rintExpected)operator([)ident(i)(])
+ keyword(assert) pre_type(Math)operator(.)ident(floor)operator(()ident(val)(\)) operator(==) ident(floorExpected)operator([)ident(i)(])
+ keyword(assert) pre_type(Math)operator(.)ident(ceil)operator(()ident(val)(\)) operator(==) ident(ceilExpected)operator([)ident(i)(])
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.4)
+comment(//----------------------------------------------------------------------------------)
+keyword(assert) pre_type(Integer)operator(.)ident(parseInt)operator(()string<delimiter(')content(0110110)delimiter(')>operator(,) integer(2)(\)) operator(==) integer(54)
+keyword(assert) pre_type(Integer)operator(.)ident(toString)operator(()integer(54)operator(,) integer(2)(\)) operator(==) string<delimiter(')content(110110)delimiter(')>
+comment(// also works for other radix values, e.g. hex)
+keyword(assert) pre_type(Integer)operator(.)ident(toString)operator(()integer(60)operator(,) integer(16)(\)) operator(==) string<delimiter(')content(3c)delimiter(')>
+
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.5)
+comment(//----------------------------------------------------------------------------------)
+ident(x) operator(=) integer(3)operator(;) ident(y) operator(=) integer(20)
+keyword(for) operator(()ident(i) keyword(in) ident(x)operator(..)ident(y)(\)) operator({)
+ comment(//i is set to every integer from x to y, inclusive)
+(})
+
+operator(()ident(x)operator(..<)ident(y)(\))operator(.)ident(each) operator({)
+ comment(//implicit closure variable it is set to every integer from x up to but excluding y)
+(})
+
+keyword(assert) operator(()ident(x)operator(..)ident(y)(\))operator(.)ident(step)operator(()integer(7)(\)) operator(==) operator([)integer(3)operator(,) integer(10)operator(,) integer(17)(])
+
+ident(years) operator(=) type([])
+operator(()integer(5)operator(..<)integer(13)(\))operator(.)ident(each)operator({) ident(age) operator(->) ident(years) operator(+=) ident(age) (})
+keyword(assert) ident(years) operator(==) operator([)integer(5)operator(,) integer(6)operator(,) integer(7)operator(,) integer(8)operator(,) integer(9)operator(,) integer(10)operator(,) integer(11)operator(,) integer(12)(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// We can add additional methods to the Integer class)
+type(class) class(IntegerCategory) operator({)
+ directive(static) keyword(def) ident(romanMap) operator(=) operator([)integer(1000)operator(:)string<delimiter(')content(M)delimiter(')>operator(,) integer(900)operator(:)string<delimiter(')content(CM)delimiter(')>operator(,) integer(500)operator(:)string<delimiter(')content(D)delimiter(')>operator(,) integer(400)operator(:)string<delimiter(')content(CD)delimiter(')>operator(,) integer(100)operator(:)string<delimiter(')content(C)delimiter(')>operator(,) integer(90)operator(:)string<delimiter(')content(XC)delimiter(')>operator(,)
+ integer(50)operator(:)string<delimiter(')content(L)delimiter(')>operator(,) integer(40)operator(:)string<delimiter(')content(XL)delimiter(')>operator(,) integer(10)operator(:)string<delimiter(')content(X)delimiter(')>operator(,) integer(9)operator(:)string<delimiter(')content(IX)delimiter(')>operator(,) integer(5)operator(:)string<delimiter(')content(V)delimiter(')>operator(,) integer(4)operator(:)string<delimiter(')content(IV)delimiter(')>operator(,) integer(1)operator(:)string<delimiter(')content(I)delimiter(')>(])
+
+ directive(static) ident(getRoman)operator(()pre_type(Integer) ident(self)(\)) operator({)
+ keyword(def) ident(remains) operator(=) ident(self)
+ keyword(def) ident(text) operator(=) string<delimiter(')delimiter(')>
+ ident(romanMap)operator(.)ident(keySet)operator(()(\))operator(.)ident(sort)operator(()(\))operator(.)ident(reverse)operator(()(\))operator(.)ident(each)operator({) ident(key) operator(->)
+ keyword(while) operator(()ident(remains) operator(>=) ident(key)(\)) operator({)
+ ident(remains) operator(-=) ident(key)
+ ident(text) operator(+=) ident(romanMap)operator([)ident(key)(])
+ (})
+ (})
+ keyword(return) ident(text)
+ (})
+
+ directive(static) type(int) ident(parseRoman)operator(()pre_type(Object) ident(self)operator(,) pre_type(String) ident(input)(\)) operator({)
+ keyword(def) ident(ustr) operator(=) ident(input)operator(.)ident(toUpperCase)operator(()(\))
+ type(int) ident(sum) operator(=) integer(0)
+ ident(romanMap)operator(.)ident(keySet)operator(()(\))operator(.)ident(sort)operator(()(\))operator(.)ident(reverse)operator(()(\))operator(.)ident(each)operator({) ident(key) operator(->)
+ keyword(while) operator(()ident(ustr)operator(.)ident(startsWith)operator(()ident(romanMap)operator([)ident(key)(]\)\)) operator({)
+ ident(sum) operator(+=) ident(key)
+ ident(ustr) operator(-=) ident(romanMap)operator([)ident(key)(])
+ (})
+ (})
+ keyword(return) ident(sum)
+ (})
+(})
+
+ident(use)operator(()ident(IntegerCategory)(\)) operator({)
+ type(int) ident(fifteen) operator(=) integer(15)
+ keyword(assert) ident(fifteen)operator(.)ident(roman) operator(==) string<delimiter(')content(XV)delimiter(')>
+ keyword(assert) ident(parseRoman)operator(()string<delimiter(')content(XXVI)delimiter(')>(\)) operator(==) integer(26)
+ keyword(for) operator(()ident(i) keyword(in) integer(1)operator(..)integer(3900)(\)) operator({)
+ keyword(assert) ident(i) operator(==) ident(parseRoman)operator(()ident(i)operator(.)ident(roman)(\))
+ (})
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.7)
+comment(//----------------------------------------------------------------------------------)
+ident(random) operator(=) keyword(new) pre_type(Random)operator(()(\))
+integer(100)operator(.)ident(times)operator({)
+ ident(next) operator(=) ident(random)operator(.)ident(nextInt)operator(()integer(50)(\)) operator(+) integer(25)
+ keyword(assert) ident(next) operator(>) integer(24)
+ keyword(assert) ident(next) operator(<) integer(76)
+(})
+ident(chars) operator(=) type([])
+operator([)string<delimiter(')content(A)delimiter(')>operator(..)string<delimiter(')content(Z)delimiter(')>operator(,)string<delimiter(')content(a)delimiter(')>operator(..)string<delimiter(')content(z)delimiter(')>operator(,)string<delimiter(')content(0)delimiter(')>operator(..)string<delimiter(')content(9)delimiter(')>operator(,)operator(()string<delimiter(')content(!@)content($)content(%^&*)delimiter(')> keyword(as) pre_type(String)type([])(\))operator(.)ident(toList)operator(()(\)])operator(.)ident(each)operator({)ident(chars) operator(+=) local_variable(it)(})
+ident(password) operator(=) operator(()integer(1)operator(..)integer(8)(\))operator(.)ident(collect)operator({) ident(chars)operator([)ident(random)operator(.)ident(nextInt)operator(()ident(chars)operator(.)ident(size)operator(()(\)\)]) (})operator(.)ident(join)operator(()(\))
+keyword(assert) ident(password)operator(.)ident(size)operator(()(\)) operator(==) integer(8)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.8)
+comment(//----------------------------------------------------------------------------------)
+comment(// By default Groovy uses Java's Random facilities which use the current time)
+comment(// as the initial seed. This always changes but does so slowly over time.)
+comment(// You are free to select a better seed if you want greater randomness or)
+comment(// use the same one each time if you need repeatability.)
+type(long) ident(seed) operator(=) pre_type(System)operator(.)ident(currentTimeMillis)operator(()(\))
+ident(random1) operator(=) keyword(new) pre_type(Random)operator(()ident(seed)(\))
+ident(random2) operator(=) keyword(new) pre_type(Random)operator(()ident(seed)(\))
+keyword(assert) ident(random1)operator(.)ident(nextInt)operator(()(\)) operator(==) ident(random2)operator(.)ident(nextInt)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.9)
+comment(//----------------------------------------------------------------------------------)
+comment(// java.util.Random which Groovy uses already uses a 48-bit seed)
+comment(// You can make use 64 not 48 bits (and make better use of the 48 bits\) see here:)
+comment(// http://alife.co.uk/nonrandom/)
+comment(// You can choose a better seed, e.g. Ant uses:)
+ident(seed) operator(=) pre_type(System)operator(.)ident(currentTimeMillis)operator(()(\)) operator(+) pre_type(Runtime)operator(.)ident(runtime)operator(.)ident(freeMemory)operator(()(\))
+comment(// You can accept input from the user, e.g.)
+comment(// http://examples.oreilly.com/javacrypt/files/oreilly/jonathan/util/Seeder.java)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.10)
+comment(//----------------------------------------------------------------------------------)
+comment(// use Java's Random.nextGaussian(\) method)
+ident(random) operator(=) keyword(new) pre_type(Random)operator(()(\))
+ident(mean) operator(=) integer(25)
+ident(sdev) operator(=) integer(2)
+ident(salary) operator(=) ident(random)operator(.)ident(nextGaussian)operator(()(\)) operator(*) ident(sdev) operator(+) ident(mean)
+comment(// script:)
+ident(printf) string<delimiter(')content(You have been hired at )content(\\$)content(%.2f)delimiter(')>operator(,) ident(salary)
+comment(// => You have been hired at $25.05)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// radians = Math.toRadians(degrees\))
+keyword(assert) pre_type(Math)operator(.)ident(toRadians)operator(()integer(90)(\)) operator(==) pre_type(Math)operator(.)ident(PI) operator(/) integer(2)
+comment(// degrees = Math.toDegrees(radians\))
+keyword(assert) pre_type(Math)operator(.)ident(toDegrees)operator(()pre_type(Math)operator(.)ident(PI)(\)) operator(==) integer(180)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.12)
+comment(//----------------------------------------------------------------------------------)
+comment(// use Java's trigonometry methods in java.lang.Math)
+comment(//----------------------------------------------------------------------------------)
+ident(t) operator(=) pre_type(Math)operator(.)ident(tan)operator(()float(1.5)(\))
+keyword(assert) ident(t) operator(>) float(14.1) operator(&&) ident(t) operator(<) float(14.11)
+ident(ac) operator(=) pre_type(Math)operator(.)ident(acos)operator(()float(0.1)(\))
+keyword(assert) ident(ac) operator(>) float(1.47) operator(&&) ident(ac) operator(<) float(1.48)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.13)
+comment(//----------------------------------------------------------------------------------)
+keyword(assert) pre_type(Math)operator(.)ident(log)operator(()pre_type(Math)operator(.)ident(E)(\)) operator(==) integer(1)
+keyword(assert) pre_type(Math)operator(.)ident(log10)operator(()integer(10000)(\)) operator(==) integer(4)
+keyword(def) method(logn)operator(()ident(base)operator(,) ident(val)(\)) operator({) pre_type(Math)operator(.)ident(log)operator(()ident(val)(\))operator(/)pre_type(Math)operator(.)ident(log)operator(()ident(base)(\)) (})
+keyword(assert) ident(logn)operator(()integer(2)operator(,) integer(1024)(\)) operator(==) integer(10)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.14)
+comment(//----------------------------------------------------------------------------------)
+comment(// there are several Java Matrix packages available, e.g.)
+comment(// http://math.nist.gov/javanumerics/jama)
+keyword(import) include(Jama.Matrix)
+ident(matrix1) operator(=) keyword(new) ident(Matrix)operator(()operator([)
+ operator([)integer(3)operator(,) integer(2)operator(,) integer(3)(])operator(,)
+ operator([)integer(5)operator(,) integer(9)operator(,) integer(8)(])
+(]) keyword(as) type(double)type([])type([])(\))
+
+ident(matrix2) operator(=) keyword(new) ident(Matrix)operator(()operator([)
+ operator([)integer(4)operator(,) integer(7)(])operator(,)
+ operator([)integer(9)operator(,) integer(3)(])operator(,)
+ operator([)integer(8)operator(,) integer(1)(])
+(]) keyword(as) type(double)type([])type([])(\))
+
+ident(expectedArray) operator(=) operator([)operator([)float(54.0)operator(,) float(30.0)(])operator(,) operator([)float(165.0)operator(,) float(70.0)(]]) keyword(as) type(double)type([])type([])
+ident(productArray) operator(=) ident(matrix1)operator(.)ident(times)operator(()ident(matrix2)(\))operator(.)ident(array)
+
+keyword(for) operator(()ident(i) keyword(in) integer(0)operator(..<)ident(productArray)operator(.)ident(size)operator(()(\)\)) operator({)
+ keyword(assert) ident(productArray)operator([)ident(i)(]) operator(==) ident(expectedArray)operator([)ident(i)(])
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.15)
+comment(//----------------------------------------------------------------------------------)
+comment(// there are several Java Complex number packages, e.g.:)
+comment(// http://jakarta.apache.org/commons/math/userguide/complex.html)
+keyword(import) include(org.apache.commons.math.complex.Complex)
+ident(a) operator(=) keyword(new) ident(Complex)operator(()integer(3)operator(,) integer(5)(\)) comment(// 3 + 5i)
+ident(b) operator(=) keyword(new) ident(Complex)operator(()integer(2)operator(,) operator(-)integer(2)(\)) comment(// 2 - 2i)
+ident(expected) operator(=) keyword(new) ident(Complex) operator(()integer(16)operator(,) integer(4)(\)) comment(// 16 + 4i)
+keyword(assert) ident(expected) operator(==) ident(a) operator(*) ident(b)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.16)
+comment(//----------------------------------------------------------------------------------)
+keyword(assert) pre_type(Integer)operator(.)ident(parseInt)operator(()string<delimiter(')content(101)delimiter(')>operator(,) integer(16)(\)) operator(==) integer(257)
+keyword(assert) pre_type(Integer)operator(.)ident(parseInt)operator(()string<delimiter(')content(077)delimiter(')>operator(,) integer(8)(\)) operator(==) integer(63)
+comment(//----------------------------------------------------------------------------------)
+comment(// conversionScript:)
+ident(print) string<delimiter(')content(Gimme a number in decimal, octal, or hex: )delimiter(')>
+ident(reader) operator(=) keyword(new) pre_type(BufferedReader)operator(()keyword(new) pre_type(InputStreamReader)operator(()pre_type(System)operator(.)ident(in)(\)\))
+ident(input) operator(=) ident(reader)operator(.)ident(readLine)operator(()(\))operator(.)ident(trim)operator(()(\))
+keyword(switch)operator(()ident(input)(\)) operator({)
+ keyword(case) operator(~)string<delimiter(')content(^0x)char(\\\\)content(d+)delimiter(')>operator(:)
+ ident(number) operator(=) pre_type(Integer)operator(.)ident(parseInt)operator(()ident(input)operator(.)ident(substring)operator(()integer(2)(\))operator(,) integer(16)(\))operator(;) keyword(break)
+ keyword(case) operator(~)string<delimiter(')content(^0)char(\\\\)content(d+)delimiter(')>operator(:)
+ ident(number) operator(=) pre_type(Integer)operator(.)ident(parseInt)operator(()ident(input)operator(.)ident(substring)operator(()integer(1)(\))operator(,) integer(8)(\))operator(;) keyword(break)
+ keyword(default)operator(:)
+ ident(number) operator(=) pre_type(Integer)operator(.)ident(parseInt)operator(()ident(input)(\))
+(})
+ident(println) string<delimiter(')content(Decimal value: )delimiter(')> operator(+) ident(number)
+
+comment(// permissionScript:)
+ident(print) string<delimiter(')content(Enter file permission in octal: )delimiter(')>
+ident(input) operator(=) keyword(new) pre_type(BufferedReader)operator(()keyword(new) pre_type(InputStreamReader)operator(()pre_type(System)operator(.)ident(in)(\)\))
+ident(num) operator(=) ident(input)operator(.)ident(readLine)operator(()(\))operator(.)ident(trim)operator(()(\))
+ident(permission) operator(=) pre_type(Integer)operator(.)ident(parseInt)operator(()ident(num)operator(,) integer(8)(\))
+ident(println) string<delimiter(')content(Decimal value: )delimiter(')> operator(+) ident(permission)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.17)
+comment(//----------------------------------------------------------------------------------)
+ident(nf) operator(=) pre_type(NumberFormat)operator(.)ident(getInstance)operator(()(\))
+keyword(assert) ident(nf)operator(.)ident(format)operator(()operator(-)integer(1740525205)(\)) operator(==) string<delimiter(')content(-1,740,525,205)delimiter(')>
+comment(//----------------------------------------------------------------------------------)
+comment(// @@PLEAC@@_2.18)
+comment(//----------------------------------------------------------------------------------)
+keyword(def) method(timeMessage)operator(()ident(hour)(\)) operator({) string<delimiter(')content(It took )delimiter(')> operator(+) ident(hour) operator(+) string<delimiter(')content( hour)delimiter(')> operator(+) operator(()ident(hour) operator(==) integer(1) operator(?) string<delimiter(')delimiter(')> operator(:) string<delimiter(')content(s)delimiter(')>(\)) (})
+keyword(assert) string<delimiter(')content(It took 1 hour)delimiter(')> operator(==) ident(timeMessage)operator(()integer(1)(\))
+keyword(assert) string<delimiter(')content(It took 2 hours)delimiter(')> operator(==) ident(timeMessage)operator(()integer(2)(\))
+
+comment(// you can also use Java's ChoiceFormat)
+comment(// overkill for this example but extensible and compatible with MessageFormat)
+ident(limits) operator(=) operator([)integer(1)operator(,) pre_type(ChoiceFormat)operator(.)ident(nextDouble)operator(()integer(1)(\)]) keyword(as) type(double)type([])
+ident(names) operator(=) operator([)string<delimiter(')content(century)delimiter(')>operator(,) string<delimiter(')content(centuries)delimiter(')>(]) keyword(as) pre_type(String)type([])
+ident(choice) operator(=) keyword(new) pre_type(ChoiceFormat)operator(()ident(limits)operator(,) ident(names)(\))
+ident(numCenturies) operator(=) integer(1)
+ident(expected) operator(=) string<delimiter(')content(It took 1 century)delimiter(')>
+keyword(assert) ident(expected) operator(==) string<delimiter(")content(It took )inline<inline_delimiter($)ident(numCenturies)>content( )delimiter(")> operator(+) ident(choice)operator(.)ident(format)operator(()ident(numCenturies)(\))
+comment(// an alternate constructor syntax)
+ident(choice) operator(=) keyword(new) pre_type(ChoiceFormat)operator(()string<delimiter(')content(0#are no files|1#is one file|2#are multiple files)delimiter(')>(\))
+keyword(assert) ident(choice)operator(.)ident(format)operator(()integer(3)(\)) operator(==) string<delimiter(')content(are multiple files)delimiter(')>
+
+comment(// more complex pluralization can be done with Java libraries, e.g.:)
+comment(// http://www.elvis.ac.nz/brain?PluralizationMapping)
+comment(// org.springframework.util.Pluralizer within the Spring Framework (springframework.org\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_2.19)
+comment(//----------------------------------------------------------------------------------)
+comment(// calculating prime factors)
+keyword(def) method(factorize)operator(()pre_type(BigInteger) ident(orig)(\)) operator({)
+ ident(factors) operator(=) operator([)operator(:)(])
+ keyword(def) ident(addFactor) operator(=) operator({) ident(x) operator(->) keyword(if) operator(()ident(factors)operator([)ident(x)(]\)) ident(factors)operator([)ident(x)(]) operator(+=) integer(1) keyword(else) ident(factors)operator([)ident(x)(]) operator(=) integer(1) (})
+ ident(n) operator(=) ident(orig)
+ ident(i) operator(=) integer(2)
+ ident(sqi) operator(=) integer(4) comment(// square of i)
+ keyword(while) operator(()ident(sqi) operator(<=) ident(n)(\)) operator({)
+ keyword(while) operator(()ident(n)operator(.)ident(remainder)operator(()ident(i)(\)) operator(==) integer(0)(\)) operator({)
+ ident(n) operator(/)operator(=) ident(i)
+ ident(addFactor) ident(i)
+ (})
+ comment(// we take advantage of the fact that (i+1\)**2 = i**2 + 2*i + 1)
+ ident(sqi) operator(+=) integer(2) operator(*) ident(i) operator(+) integer(1)
+ ident(i) operator(+=) integer(1)
+ (})
+ keyword(if) operator(()operator(()ident(n) operator(!=) integer(1)(\)) operator(&&) operator(()ident(n) operator(!=) ident(orig)(\)\)) ident(addFactor) ident(n)
+ keyword(return) ident(factors)
+(})
+
+keyword(def) method(pretty)operator(()ident(factors)(\)) operator({)
+ keyword(if) operator(()operator(!)ident(factors)(\)) keyword(return) string<delimiter(")content(PRIME)delimiter(")>
+ ident(sb) operator(=) keyword(new) pre_type(StringBuffer)operator(()(\))
+ ident(factors)operator(.)ident(keySet)operator(()(\))operator(.)ident(sort)operator(()(\))operator(.)ident(each) operator({) ident(key) operator(->)
+ ident(sb) operator(<)operator(<) ident(key)
+ keyword(if) operator(()ident(factors)operator([)ident(key)(]) operator(>) integer(1)(\)) ident(sb) operator(<)operator(<) string<delimiter(")content(**)delimiter(")> operator(+) ident(factors)operator([)ident(key)(])
+ ident(sb) operator(<)operator(<) string<delimiter(")content( )delimiter(")>
+ (})
+ keyword(return) ident(sb)operator(.)ident(toString)operator(()(\))operator(.)ident(trim)operator(()(\))
+(})
+
+keyword(assert) ident(pretty)operator(()ident(factorize)operator(()integer(2178)(\)\)) operator(==) string<delimiter(')content(2 3**2 11**2)delimiter(')>
+keyword(assert) ident(pretty)operator(()ident(factorize)operator(()integer(39887)(\)\)) operator(==) string<delimiter(')content(PRIME)delimiter(')>
+keyword(assert) ident(pretty)operator(()ident(factorize)operator(()integer(239322000000000000000000)(\)\)) operator(==) string<delimiter(')content(2**19 3 5**18 39887)delimiter(')>
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_3.0)
+comment(//----------------------------------------------------------------------------------)
+comment(// use Date to get the current time)
+ident(println) keyword(new) pre_type(Date)operator(()(\))
+comment(// => Mon Jan 01 07:12:32 EST 2007)
+comment(// use Calendar to compute year, month, day, hour, minute, and second values)
+ident(cal) operator(=) pre_type(Calendar)operator(.)ident(instance)
+ident(println) string<delimiter(')content(Today is day )delimiter(')> operator(+) ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(DAY_OF_YEAR)(\)) operator(+) string<delimiter(')content( of the current year.)delimiter(')>
+comment(// => Today is day 1 of the current year.)
+comment(// there are other Java Date/Time packages with extended capabilities, e.g.:)
+comment(// http://joda-time.sourceforge.net/)
+comment(// there is a special Grails (grails.codehaus.org\) time DSL (see below\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_3.1)
+comment(//----------------------------------------------------------------------------------)
+ident(cal) operator(=) pre_type(Calendar)operator(.)ident(instance)
+ident(Y) operator(=) ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(YEAR)(\))
+ident(M) operator(=) ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(MONTH)(\)) operator(+) integer(1)
+ident(D) operator(=) ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(DATE)(\))
+ident(println) string<delimiter(")content(The current date is )inline<inline_delimiter($)ident(Y)>content( )inline<inline_delimiter($)ident(M)>content( )inline<inline_delimiter($)ident(D)>delimiter(")>
+comment(// => The current date is 2006 04 28)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_3.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// create a calendar with current time and time zone)
+ident(cal) operator(=) pre_type(Calendar)operator(.)ident(instance)
+comment(// set time zone using long or short timezone values)
+ident(cal)operator(.)ident(timeZone) operator(=) pre_type(TimeZone)operator(.)ident(getTimeZone)operator(()string<delimiter(")content(America/Los_Angeles)delimiter(")>(\))
+ident(cal)operator(.)ident(timeZone) operator(=) pre_type(TimeZone)operator(.)ident(getTimeZone)operator(()string<delimiter(")content(UTC)delimiter(")>(\))
+comment(// set date fields one at a time)
+ident(cal)operator(.)ident(set)operator(()pre_type(Calendar)operator(.)ident(MONTH)operator(,) pre_type(Calendar)operator(.)ident(DECEMBER)(\))
+comment(// or several together)
+comment(//calendar.set(year, month - 1, day, hour, minute, second\))
+comment(// get time in seconds since EPOCH)
+type(long) ident(time) operator(=) ident(cal)operator(.)ident(time)operator(.)ident(time) operator(/) integer(1000)
+ident(println) ident(time)
+comment(// => 1196522682)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_3.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// create a calendar with current time and time zone)
+ident(cal) operator(=) pre_type(Calendar)operator(.)ident(instance)
+comment(// set time)
+ident(cal)operator(.)ident(time) operator(=) keyword(new) pre_type(Date)operator(()ident(time) operator(*) integer(1000)(\))
+comment(// get date fields)
+ident(println)operator(()string<delimiter(')content(Dateline: )delimiter(')>
+ operator(+) ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(HOUR_OF_DAY)(\)) operator(+) string<delimiter(')content(:)delimiter(')>
+ operator(+) ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(MINUTE)(\)) operator(+) string<delimiter(')content(:)delimiter(')>
+ operator(+) ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(SECOND)(\)) operator(+) string<delimiter(')content(-)delimiter(')>
+ operator(+) ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(YEAR)(\)) operator(+) string<delimiter(')content(/)delimiter(')>
+ operator(+) operator(()ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(MONTH)(\)) operator(+) integer(1)(\)) operator(+) string<delimiter(')content(/)delimiter(')>
+ operator(+) ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(DATE)(\)\))
+comment(// => Dateline: 7:33:16-2007/1/1)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_3.4)
+comment(//----------------------------------------------------------------------------------)
+keyword(import) include(java.text.SimpleDateFormat)
+type(long) ident(difference) operator(=) integer(100)
+type(long) ident(after) operator(=) ident(time) operator(+) ident(difference)
+type(long) ident(before) operator(=) ident(time) operator(-) ident(difference)
+
+comment(// any field of a calendar is incrementable via add(\) and roll(\) methods)
+ident(cal) operator(=) pre_type(Calendar)operator(.)ident(instance)
+ident(df) operator(=) keyword(new) pre_type(SimpleDateFormat)operator(()(\))
+ident(printCal) operator(=) operator({)ident(cal) operator(->) ident(df)operator(.)ident(format)operator(()ident(cal)operator(.)ident(time)(\)})
+ident(cal)operator(.)ident(set)operator(()integer(2000)operator(,) integer(0)operator(,) integer(1)operator(,) oct(00)operator(,) oct(01)operator(,) integer(0)(\))
+keyword(assert) ident(printCal)operator(()ident(cal)(\)) operator(==) string<delimiter(')content(1/01/00 00:01)delimiter(')>
+comment(// roll minute back by 2 but don't adjust other fields)
+ident(cal)operator(.)ident(roll)operator(()pre_type(Calendar)operator(.)ident(MINUTE)operator(,) operator(-)integer(2)(\))
+keyword(assert) ident(printCal)operator(()ident(cal)(\)) operator(==) string<delimiter(')content(1/01/00 00:59)delimiter(')>
+comment(// adjust hour back 1 and adjust other fields if needed)
+ident(cal)operator(.)ident(add)operator(()pre_type(Calendar)operator(.)ident(HOUR)operator(,) operator(-)integer(1)(\))
+keyword(assert) ident(printCal)operator(()ident(cal)(\)) operator(==) string<delimiter(')content(31/12/99 23:59)delimiter(')>
+
+comment(// larger example)
+ident(cal)operator(.)ident(timeZone) operator(=) pre_type(TimeZone)operator(.)ident(getTimeZone)operator(()string<delimiter(")content(UTC)delimiter(")>(\))
+ident(cal)operator(.)ident(set)operator(()integer(1973)operator(,) integer(0)operator(,) integer(18)operator(,) integer(3)operator(,) integer(45)operator(,) integer(50)(\))
+ident(cal)operator(.)ident(add)operator(()pre_type(Calendar)operator(.)ident(DATE)operator(,) integer(55)(\))
+ident(cal)operator(.)ident(add)operator(()pre_type(Calendar)operator(.)ident(HOUR_OF_DAY)operator(,) integer(2)(\))
+ident(cal)operator(.)ident(add)operator(()pre_type(Calendar)operator(.)ident(MINUTE)operator(,) integer(17)(\))
+ident(cal)operator(.)ident(add)operator(()pre_type(Calendar)operator(.)ident(SECOND)operator(,) integer(5)(\))
+keyword(assert) ident(printCal)operator(()ident(cal)(\)) operator(==) string<delimiter(')content(14/03/73 16:02)delimiter(')>
+
+comment(// alternatively, work with epoch times)
+type(long) ident(birthTime) operator(=) integer(96176750359) comment(// 18/Jan/1973, 3:45:50 am)
+type(long) ident(interval) operator(=) integer(5) operator(+) comment(// 5 second)
+ integer(17) operator(*) integer(60) operator(+) comment(// 17 minute)
+ integer(2) operator(*) integer(60) operator(*) integer(60) operator(+) comment(// 2 hour)
+ integer(55) operator(*) integer(60) operator(*) integer(60) operator(*) integer(24) comment(// and 55 day)
+ident(then) operator(=) keyword(new) pre_type(Date)operator(()ident(birthTime) operator(+) ident(interval) operator(*) integer(1000)(\))
+keyword(assert) ident(df)operator(.)ident(format)operator(()ident(then)(\)) operator(==) string<delimiter(')content(14/03/73 16:02)delimiter(')>
+
+comment(// Alternatively, the Google Data module has a category with DSL-like time support:)
+comment(// http://docs.codehaus.org/display/GROOVY/Google+Data+Support)
+comment(// which supports the following syntax)
+comment(// def interval = 5.seconds + 17.minutes + 2.hours + 55.days)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_3.5)
+comment(//----------------------------------------------------------------------------------)
+ident(bree) operator(=) integer(361535725) comment(// 16 Jun 1981, 4:35:25)
+ident(nat) operator(=) integer(96201950) comment(// 18 Jan 1973, 3:45:50)
+ident(difference) operator(=) ident(bree) operator(-) ident(nat)
+ident(println) string<delimiter(")content(There were )inline<inline_delimiter($)ident(difference)>content( seconds between Nat and Bree)delimiter(")>
+comment(// => There were 265333775 seconds between Nat and Bree)
+ident(seconds) operator(=) ident(difference) operator(%) integer(60)
+ident(difference) operator(=) operator(()ident(difference) operator(-) ident(seconds)(\)) operator(/) integer(60)
+ident(minutes) operator(=) ident(difference) operator(%) integer(60)
+ident(difference) operator(=) operator(()ident(difference) operator(-) ident(minutes)(\)) operator(/) integer(60)
+ident(hours) operator(=) ident(difference) operator(%) integer(24)
+ident(difference) operator(=) operator(()ident(difference) operator(-) ident(hours)(\)) operator(/) integer(24)
+ident(days) operator(=) ident(difference) operator(%) integer(7)
+ident(weeks) operator(=) operator(()ident(difference) operator(-) ident(days)(\)) operator(/) integer(7)
+ident(println) string<delimiter(")content(()inline<inline_delimiter($)ident(weeks)>content( weeks, )inline<inline_delimiter($)ident(days)>content( days, )inline<inline_delimiter($)ident(hours)>content(:)inline<inline_delimiter($)ident(minutes)>content(:)inline<inline_delimiter($)ident(seconds)>content(\))delimiter(")>
+comment(// => (438 weeks, 4 days, 23:49:35\))
+comment(//----------------------------------------------------------------------------------)
+ident(cal) operator(=) pre_type(Calendar)operator(.)ident(getInstance)operator(()pre_type(TimeZone)operator(.)ident(getTimeZone)operator(()string<delimiter(")content(UTC)delimiter(")>(\)\))
+ident(cal)operator(.)ident(set)operator(()integer(1981)operator(,) integer(5)operator(,) integer(16)(\)) comment(// 16 Jun 1981)
+ident(date1) operator(=) ident(cal)operator(.)ident(time)
+ident(cal)operator(.)ident(set)operator(()integer(1973)operator(,) integer(0)operator(,) integer(18)(\)) comment(// 18 Jan 1973)
+ident(date2) operator(=) ident(cal)operator(.)ident(time)
+ident(difference) operator(=) pre_type(Math)operator(.)ident(abs)operator(()ident(date2)operator(.)ident(time) operator(-) ident(date1)operator(.)ident(time)(\))
+ident(days) operator(=) ident(difference) operator(/) operator(()integer(1000) operator(*) integer(60) operator(*) integer(60) operator(*) integer(24)(\))
+keyword(assert) ident(days) operator(==) integer(3071)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_3.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// create a calendar with current time and time zone)
+ident(cal) operator(=) pre_type(Calendar)operator(.)ident(instance)
+ident(cal)operator(.)ident(set)operator(()integer(1981)operator(,) integer(5)operator(,) integer(16)(\))
+ident(yearDay) operator(=) ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(DAY_OF_YEAR)(\))operator(;)
+ident(year) operator(=) ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(YEAR)(\))operator(;)
+ident(yearWeek) operator(=) ident(cal)operator(.)ident(get)operator(()pre_type(Calendar)operator(.)ident(WEEK_OF_YEAR)(\))operator(;)
+ident(df1) operator(=) keyword(new) pre_type(SimpleDateFormat)operator(()string<delimiter(")content(dd/MMM/yy)delimiter(")>(\))
+ident(df2) operator(=) keyword(new) pre_type(SimpleDateFormat)operator(()string<delimiter(")content(EEEE)delimiter(")>(\))
+ident(print)operator(()ident(df1)operator(.)ident(format)operator(()ident(cal)operator(.)ident(time)(\)) operator(+) string<delimiter(')content( was a )delimiter(')> operator(+) ident(df2)operator(.)ident(format)operator(()ident(cal)operator(.)ident(time)(\)\))
+ident(println) string<delimiter(")content( and was day number )inline<inline_delimiter($)ident(yearDay)>content( and week number )inline<inline_delimiter($)ident(yearWeek)>content( of )inline<inline_delimiter($)ident(year)>delimiter(")>
+comment(// => 16/Jun/81 was a Tuesday and was day number 167 and week number 25 of 1981)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_3.7)
+comment(//----------------------------------------------------------------------------------)
+ident(input) operator(=) string<delimiter(")content(1998-06-03)delimiter(")>
+ident(df1) operator(=) keyword(new) pre_type(SimpleDateFormat)operator(()string<delimiter(")content(yyyy-MM-dd)delimiter(")>(\))
+ident(date) operator(=) ident(df1)operator(.)ident(parse)operator(()ident(input)(\))
+ident(df2) operator(=) keyword(new) pre_type(SimpleDateFormat)operator(()string<delimiter(")content(MMM/dd/yyyy)delimiter(")>(\))
+ident(println) string<delimiter(')content(Date was )delimiter(')> operator(+) ident(df2)operator(.)ident(format)operator(()ident(date)(\))
+comment(// => Date was Jun/03/1998)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_3.8)
+comment(//----------------------------------------------------------------------------------)
+keyword(import) include(java.text.DateFormat)
+ident(df) operator(=) keyword(new) pre_type(SimpleDateFormat)operator(()string<delimiter(')content(E M d hh:mm:ss z yyyy)delimiter(')>(\))
+ident(cal)operator(.)ident(set)operator(()integer(2007)operator(,) integer(0)operator(,) integer(1)(\))
+ident(println) string<delimiter(')content(Customized format gives: )delimiter(')> operator(+) ident(df)operator(.)ident(format)operator(()ident(cal)operator(.)ident(time)(\))
+comment(// => Mon 1 1 09:02:29 EST 2007 (differs depending on your timezone\))
+ident(df) operator(=) pre_type(DateFormat)operator(.)ident(getDateInstance)operator(()pre_type(DateFormat)operator(.)ident(FULL)operator(,) pre_type(Locale)operator(.)ident(FRANCE)(\))
+ident(println) string<delimiter(')content(Customized format gives: )delimiter(')> operator(+) ident(df)operator(.)ident(format)operator(()ident(cal)operator(.)ident(time)(\))
+comment(// => lundi 1 janvier 2007)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_3.9)
+comment(//----------------------------------------------------------------------------------)
+comment(// script:)
+ident(println) string<delimiter(')content(Press return when ready)delimiter(')>
+ident(before) operator(=) pre_type(System)operator(.)ident(currentTimeMillis)operator(()(\))
+ident(input) operator(=) keyword(new) pre_type(BufferedReader)operator(()keyword(new) pre_type(InputStreamReader)operator(()pre_type(System)operator(.)ident(in)(\)\))operator(.)ident(readLine)operator(()(\))
+ident(after) operator(=) pre_type(System)operator(.)ident(currentTimeMillis)operator(()(\))
+ident(elapsed) operator(=) operator(()ident(after) operator(-) ident(before)(\)) operator(/) integer(1000)
+ident(println) string<delimiter(")content(You took )inline<inline_delimiter($)ident(elapsed)>content( seconds.)delimiter(")>
+comment(// => You took2.313 seconds.)
+
+comment(// take mean sorting time)
+ident(size) operator(=) integer(500)operator(;) ident(number) operator(=) integer(100)operator(;) ident(total) operator(=) integer(0)
+keyword(for) operator(()ident(i) keyword(in) integer(0)operator(..<)ident(number)(\)) operator({)
+ ident(array) operator(=) type([])
+ ident(size)operator(.)ident(times)operator({) ident(array) operator(<)operator(<) pre_type(Math)operator(.)ident(random)operator(()(\)) (})
+ ident(doubles) operator(=) ident(array) keyword(as) type(double)type([])
+ comment(// sort it)
+ type(long) ident(t0) operator(=) pre_type(System)operator(.)ident(currentTimeMillis)operator(()(\))
+ pre_type(Arrays)operator(.)ident(sort)operator(()ident(doubles)(\))
+ type(long) ident(t1) operator(=) pre_type(System)operator(.)ident(currentTimeMillis)operator(()(\))
+ ident(total) operator(+=) operator(()ident(t1) operator(-) ident(t0)(\))
+(})
+ident(println) string<delimiter(")content(On average, sorting )inline<inline_delimiter($)ident(size)>content( random numbers takes )inline<inline_delimiter(${)ident(total / number)inline_delimiter(})>content( milliseconds)delimiter(")>
+comment(// => On average, sorting 500 random numbers takes 0.32 milliseconds)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_3.10)
+comment(//----------------------------------------------------------------------------------)
+ident(delayMillis) operator(=) integer(50)
+pre_type(Thread)operator(.)ident(sleep)operator(()ident(delayMillis)(\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_3.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// this could be done more simply using JavaMail's getAllHeaderLines(\) but is shown)
+comment(// in long hand for illustrative purposes)
+ident(sampleMessage) operator(=) string<delimiter(''')content(Delivered-To: alias-someone@somewhere.com.au
+Received: (qmail 27284 invoked from network\); 30 Dec 2006 15:16:26 -0000
+Received: from unknown (HELO lists-outbound.sourceforge.net\) (66.35.250.225\)
+ by bne012m.server-web.com with SMTP; 30 Dec 2006 15:16:25 -0000
+Received: from sc8-sf-list2-new.sourceforge.net (sc8-sf-list2-new-b.sourceforge.net [10.3.1.94]\)
+ by sc8-sf-spam2.sourceforge.net (Postfix\) with ESMTP
+ id D8CCBFDE3; Sat, 30 Dec 2006 07:16:24 -0800 (PST\)
+Received: from sc8-sf-mx1-b.sourceforge.net ([10.3.1.91]
+ helo=mail.sourceforge.net\)
+ by sc8-sf-list2-new.sourceforge.net with esmtp (Exim 4.43\)
+ id 1H0fwX-0003c0-GA
+ for pleac-discuss@lists.sourceforge.net; Sat, 30 Dec 2006 07:16:20 -0800
+Received: from omta05ps.mx.bigpond.com ([144.140.83.195]\)
+ by mail.sourceforge.net with esmtp (Exim 4.44\) id 1H0fwY-0005D4-DD
+ for pleac-discuss@lists.sourceforge.net; Sat, 30 Dec 2006 07:16:19 -0800
+Received: from win2K001 ([138.130.127.127]\) by omta05ps.mx.bigpond.com
+ with SMTP
+ id <20061230151611.XVWL19269.omta05ps.mx.bigpond.com@win2K001>;
+ Sat, 30 Dec 2006 15:16:11 +0000
+From: someone@somewhere.com
+To: <pleac-discuss@lists.sourceforge.net>
+Date: Sun, 31 Dec 2006 02:14:57 +1100
+Subject: Re: [Pleac-discuss] C/Posix/GNU - @@pleac@@_10x
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: pleac-discuss-bounces@lists.sourceforge.net
+Errors-To: pleac-discuss-bounces@lists.sourceforge.net
+
+----- Original Message -----
+From: someone@somewhere.com
+To: otherperson@somewhereelse.com
+Cc: <pleac-discuss@lists.sourceforge.net>
+Sent: Wednesday, December 27, 2006 9:18 AM
+Subject: Re: [Pleac-discuss] C/Posix/GNU - @@pleac@@_10x
+
+I really like that description of PLEAC.
+)delimiter(''')>
+ident(expected) operator(=) string<delimiter(''')content(
+Sender Recipient Time Delta
+<origin> somewhere.com 01:14:57 06/12/31
+win2K001 omta05ps.mx.bigpond.com 01:14:57 06/12/31 1m 14s
+omta05ps.mx.bigpond.com mail.sourceforge.net 01:16:11 06/12/31 8s
+sc8-sf-mx1-b.sourceforge. sc8-sf-list2-new.sourcefo 01:16:19 06/12/31 1s
+sc8-sf-list2-new.sourcefo sc8-sf-spam2.sourceforge. 01:16:20 06/12/31 4s
+unknown bne012m.server-web.com 01:16:24 06/12/31 1s
+)delimiter(''')>
+
+type(class) class(MailHopDelta) operator({)
+ keyword(def) ident(headers)operator(,) ident(firstSender)operator(,) ident(firstDate)operator(,) ident(out)
+
+ ident(MailHopDelta)operator(()ident(mail)(\)) operator({)
+ ident(extractHeaders)operator(()ident(mail)(\))
+ ident(out) operator(=) keyword(new) pre_type(StringBuffer)operator(()(\))
+ keyword(def) ident(m) operator(=) operator(()ident(mail) operator(=~) regexp<delimiter(/)content((?m\)^Date:)char(\\s)content(+(.*\))delimiter(/)>(\))
+ ident(firstDate) operator(=) ident(parseDate)operator(()ident(m)operator([)integer(0)(])operator([)integer(1)(]\))
+ ident(firstSender) operator(=) operator(()ident(mail) operator(=~) regexp<delimiter(/)content((?m\)^From.*)content(\\@)content(([^)char(\\s)content(>]*\))delimiter(/)>(\))operator([)integer(0)(])operator([)integer(1)(])
+ ident(out)operator(()string<delimiter(')content(Sender Recipient Time Delta)delimiter(')>operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\)\))
+ (})
+
+ keyword(def) method(parseDate)operator(()ident(date)(\)) operator({)
+ keyword(try) operator({)
+ keyword(return) keyword(new) pre_type(SimpleDateFormat)operator(()string<delimiter(')content(EEE, dd MMM yyyy hh:mm:ss Z)delimiter(')>(\))operator(.)ident(parse)operator(()ident(date)(\))
+ (}) keyword(catch)operator(()ident(java)operator(.)ident(text)operator(.)ident(ParseException) ident(ex)(\)) operator({)(})
+ keyword(try) operator({)
+ keyword(return) keyword(new) pre_type(SimpleDateFormat)operator(()string<delimiter(')content(dd MMM yyyy hh:mm:ss Z)delimiter(')>(\))operator(.)ident(parse)operator(()ident(date)(\))
+ (}) keyword(catch)operator(()ident(java)operator(.)ident(text)operator(.)ident(ParseException) ident(ex)(\)) operator({)(})
+ keyword(try) operator({)
+ keyword(return) pre_type(DateFormat)operator(.)ident(getDateInstance)operator(()pre_type(DateFormat)operator(.)ident(FULL)(\))operator(.)ident(parse)operator(()ident(date)(\))
+ (}) keyword(catch)operator(()ident(java)operator(.)ident(text)operator(.)ident(ParseException) ident(ex)(\)) operator({)(})
+ pre_type(DateFormat)operator(.)ident(getDateInstance)operator(()pre_type(DateFormat)operator(.)ident(LONG)(\))operator(.)ident(parse)operator(()ident(date)(\))
+ (})
+
+ keyword(def) method(extractHeaders)operator(()ident(mail)(\)) operator({)
+ ident(headers) operator(=) type([])
+ keyword(def) ident(isHeader) operator(=) keyword(true)
+ keyword(def) ident(currentHeader) operator(=) string<delimiter(')delimiter(')>
+ ident(mail)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(each)operator({) ident(line) operator(->)
+ keyword(if) operator(()operator(!)ident(isHeader)(\)) keyword(return)
+ keyword(switch)operator(()ident(line)(\)) operator({)
+ keyword(case) operator(~)regexp<delimiter(/)content(^)char(\\s)content(*)content($)delimiter(/)>operator(:)
+ ident(isHeader) operator(=) keyword(false)
+ keyword(if) operator(()ident(currentHeader)(\)) ident(headers) operator(<)operator(<) ident(currentHeader)
+ keyword(break)
+ keyword(case) operator(~)regexp<delimiter(/)content(^)char(\\s)content(+.*)delimiter(/)>operator(:)
+ ident(currentHeader) operator(+=) ident(line)operator(;) keyword(break)
+ keyword(default)operator(:)
+ keyword(if) operator(()ident(currentHeader)(\)) ident(headers) operator(<)operator(<) ident(currentHeader)
+ ident(currentHeader) operator(=) ident(line)
+ (})
+ (})
+ (})
+
+ keyword(def) method(out)operator(()ident(line)(\)) operator({)
+ ident(out) operator(<)operator(<) ident(line)operator([)integer(0)(])operator([)integer(0)operator(..<)operator([)integer(25)operator(,)ident(line)operator([)integer(0)(])operator(.)ident(size)operator(()(\)])operator(.)ident(min)operator(()(\)])operator(.)ident(padRight)operator(()integer(26)(\))
+ ident(out) operator(<)operator(<) ident(line)operator([)integer(1)(])operator([)integer(0)operator(..<)operator([)integer(25)operator(,)ident(line)operator([)integer(1)(])operator(.)ident(size)operator(()(\)])operator(.)ident(min)operator(()(\)])operator(.)ident(padRight)operator(()integer(26)(\))
+ ident(out) operator(<)operator(<) ident(line)operator([)integer(2)(])operator(.)ident(padRight)operator(()integer(17)(\)) operator(+) string<delimiter(')content( )delimiter(')>
+ ident(out) operator(<)operator(<) ident(line)operator([)integer(3)(]) operator(+) string<delimiter(')content(\\n)delimiter(')>
+ (})
+
+ keyword(def) method(prettyDate)operator(()ident(date)(\)) operator({)
+ keyword(new) pre_type(SimpleDateFormat)operator(()string<delimiter(')content(hh:mm:ss yy/MM/dd)delimiter(')>(\))operator(.)ident(format)operator(()ident(date)(\))
+ (})
+
+ keyword(def) method(process)operator(()(\)) operator({)
+ ident(out)operator(()operator([)string<delimiter(')content(<origin>)delimiter(')>operator(,) ident(firstSender)operator(,) ident(prettyDate)operator(()ident(firstDate)(\))operator(,) string<delimiter(')delimiter(')>(]\))
+ keyword(def) ident(prevDate) operator(=) ident(firstDate)
+ ident(headers)operator(.)ident(grep)operator(()operator(~)regexp<delimiter(/)content(^Received:)char(\\s)content(from.*)delimiter(/)>(\))operator(.)ident(reverseEach)operator({) ident(hop) operator(->)
+ keyword(def) ident(from) operator(=) operator(()ident(hop) operator(=~) regexp<delimiter(/)content(from)char(\\s)content(+()char(\\S)content(+\)|)content(\\()content((.*?\))content(\\\))delimiter(/)>(\))operator([)integer(0)(])operator([)integer(1)(])
+ keyword(def) ident(by) operator(=) operator(()ident(hop) operator(=~) regexp<delimiter(/)content(by)char(\\s)content(+()char(\\S)content(+)content(\\.)char(\\S)content(+\))delimiter(/)>(\))operator([)integer(0)(])operator([)integer(1)(])
+ keyword(def) ident(hopDate) operator(=) ident(parseDate)operator(()ident(hop)operator([)ident(hop)operator(.)ident(lastIndexOf)operator(()string<delimiter(')content(;)delimiter(')>(\))operator(+)integer(2)operator(..)operator(-)integer(1)(]\))
+ ident(out)operator(()operator([)ident(from)operator(,) ident(by)operator(,) ident(prettyDate)operator(()ident(prevDate)(\))operator(,) ident(prettyDelta)operator(()ident(hopDate)operator(.)ident(time) operator(-) ident(prevDate)operator(.)ident(time)(\)]\))
+ ident(prevDate) operator(=) ident(hopDate)
+ (})
+ keyword(return) ident(out)operator(.)ident(toString)operator(()(\))
+ (})
+
+ keyword(def) method(prettyField)operator(()ident(secs)operator(,) ident(sign)operator(,) ident(ch)operator(,) ident(multiplier)operator(,) ident(sb)(\)) operator({)
+ keyword(def) ident(whole) operator(=) operator(()type(int)(\))operator(()ident(secs) operator(/) ident(multiplier)(\))
+ keyword(if) operator(()operator(!)ident(whole)(\)) keyword(return) integer(0)
+ ident(sb) operator(<)operator(<) string<delimiter(')delimiter(')> operator(+) operator(()ident(sign) operator(*) ident(whole)(\)) operator(+) ident(ch) operator(+) string<delimiter(')content( )delimiter(')>
+ keyword(return) ident(whole) operator(*) ident(multiplier)
+ (})
+
+ keyword(def) method(prettyDelta)operator(()ident(millis)(\)) operator({)
+ keyword(def) ident(sign) operator(=) ident(millis) operator(<) integer(0) operator(?) operator(-)integer(1) operator(:) integer(1)
+ keyword(def) ident(secs) operator(=) operator(()type(int)(\))pre_type(Math)operator(.)ident(abs)operator(()ident(millis)operator(/)integer(1000)(\))
+ keyword(def) ident(sb) operator(=) keyword(new) pre_type(StringBuffer)operator(()(\))
+ ident(secs) operator(-=) ident(prettyField)operator(()ident(secs)operator(,) ident(sign)operator(,) string<delimiter(')content(d)delimiter(')>operator(,) integer(60) operator(*) integer(60) operator(*) integer(24)operator(,) ident(sb)(\))
+ ident(secs) operator(-=) ident(prettyField)operator(()ident(secs)operator(,) ident(sign)operator(,) string<delimiter(')content(h)delimiter(')>operator(,) integer(60) operator(*) integer(60)operator(,) ident(sb)(\))
+ ident(secs) operator(-=) ident(prettyField)operator(()ident(secs)operator(,) ident(sign)operator(,) string<delimiter(')content(m)delimiter(')>operator(,) integer(60)operator(,) ident(sb)(\))
+ ident(prettyField)operator(()ident(secs)operator(,) ident(sign)operator(,) string<delimiter(')content(s)delimiter(')>operator(,) integer(1)operator(,) ident(sb)(\))
+ keyword(return) ident(sb)operator(.)ident(toString)operator(()(\))operator(.)ident(trim)operator(()(\))
+ (})
+(})
+
+keyword(assert) string<delimiter(')content(\\n)delimiter(')> operator(+) keyword(new) ident(MailHopDelta)operator(()ident(sampleMessage)(\))operator(.)ident(process)operator(()(\)) operator(==) ident(expected)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_4.0)
+comment(//----------------------------------------------------------------------------------)
+ident(simple) operator(=) operator([) string<delimiter(")content(this)delimiter(")>operator(,) string<delimiter(")content(that)delimiter(")>operator(,) string<delimiter(")content(the)delimiter(")>operator(,) string<delimiter(")content(other)delimiter(")> (])
+ident(nested) operator(=) operator([) string<delimiter(")content(this)delimiter(")>operator(,) string<delimiter(")content(that)delimiter(")>operator(,) operator([) string<delimiter(")content(the)delimiter(")>operator(,) string<delimiter(")content(other)delimiter(")> (]) (])
+keyword(assert) ident(nested)operator(.)ident(size)operator(()(\)) operator(==) integer(3)
+keyword(assert) ident(nested)operator([)integer(2)(])operator(.)ident(size)operator(()(\)) operator(==) integer(2)
+
+ident(flattenNestedToSimple) operator(=) operator([) string<delimiter(")content(this)delimiter(")>operator(,) string<delimiter(")content(that)delimiter(")>operator(,) operator([) string<delimiter(")content(the)delimiter(")>operator(,) string<delimiter(")content(other)delimiter(")> (]) (])operator(.)ident(flatten)operator(()(\))
+keyword(assert) ident(flattenNestedToSimple)operator(.)ident(size)operator(()(\)) operator(==) integer(4)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.1)
+comment(//----------------------------------------------------------------------------------)
+ident(a) operator(=) operator([) string<delimiter(")content(quick)delimiter(")>operator(,) string<delimiter(")content(brown)delimiter(")>operator(,) string<delimiter(")content(fox)delimiter(")> (])
+keyword(assert) ident(a)operator(.)ident(size)operator(()(\)) operator(==) integer(3)
+ident(a) operator(=) string<delimiter(')content(Why are you teasing me?)delimiter(')>operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))
+keyword(assert) ident(a) operator(==) operator([)string<delimiter(")content(Why)delimiter(")>operator(,) string<delimiter(")content(are)delimiter(")>operator(,) string<delimiter(")content(you)delimiter(")>operator(,) string<delimiter(")content(teasing)delimiter(")>operator(,) string<delimiter(")content(me?)delimiter(")>(])
+
+ident(removeLeadingSpaces) operator(=) operator({) local_variable(it)operator(.)ident(trim)operator(()(\)) (})
+ident(nonBlankLines) operator(=) operator({) local_variable(it) (})
+ident(lines) operator(=) string<delimiter(''')content(
+ The boy stood on the burning deck,
+ It was as hot as glass.
+)delimiter(''')>operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(collect)operator(()ident(removeLeadingSpaces)(\))operator(.)ident(findAll)operator(()ident(nonBlankLines)(\))
+
+keyword(assert) ident(lines) operator(==) operator([)string<delimiter(")content(The boy stood on the burning deck,)delimiter(")>operator(,)
+ string<delimiter(")content(It was as hot as glass.)delimiter(")>(])
+
+comment(// initialiseListFromFileScript:)
+ident(lines) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(mydata.txt)delimiter(')>(\))operator(.)ident(readLines)operator(()(\))
+
+comment(// processFileScript:)
+keyword(new) pre_type(File)operator(()string<delimiter(')content(mydata.txt)delimiter(')>(\))operator(.)ident(eachLine)operator({)
+ comment(// dosomething)
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.2)
+comment(//----------------------------------------------------------------------------------)
+ident(marbleColors) operator(=) operator([)string<delimiter(')content(red)delimiter(')>operator(,) string<delimiter(')content(green)delimiter(')>operator(,) string<delimiter(')content(yellow)delimiter(')>(])
+keyword(assert) ident(marbleColors)operator(.)ident(join)operator(()string<delimiter(')content(, )delimiter(')>(\)) operator(==) string<delimiter(')content(red, green, yellow)delimiter(')>
+
+keyword(def) method(commify)operator(()ident(items)(\)) operator({)
+ keyword(if) operator(()operator(!)ident(items)(\)) keyword(return) ident(items)
+ keyword(def) ident(sepchar) operator(=) ident(items)operator(.)ident(find)operator({) local_variable(it) operator(=~) regexp<delimiter(/)content(,)delimiter(/)> (}) operator(?) string<delimiter(')content(; )delimiter(')> operator(:) string<delimiter(')content(, )delimiter(')>
+ keyword(switch) operator(()ident(items)operator(.)ident(size)operator(()(\)\)) operator({)
+ keyword(case) integer(1)operator(:) keyword(return) ident(items)operator([)integer(0)(])
+ keyword(case) integer(2)operator(:) keyword(return) ident(items)operator(.)ident(join)operator(()string<delimiter(')content( and )delimiter(')>(\))
+ (})
+ ident(items)operator([)integer(0)operator(..)operator(-)integer(2)(])operator(.)ident(join)operator(()ident(sepchar)(\)) operator(+) ident(sepchar) operator(+) string<delimiter(')content(and )delimiter(')> operator(+) ident(items)operator([)operator(-)integer(1)(])
+(})
+
+keyword(assert) ident(commify)operator(()ident(marbleColors)(\)) operator(==) string<delimiter(')content(red, green, and yellow)delimiter(')>
+
+ident(lists) operator(=) operator([)
+ operator([) string<delimiter(')content(just one thing)delimiter(')> (])operator(,)
+ operator([) string<delimiter(')content(Mutt)delimiter(')>operator(,) string<delimiter(')content(Jeff)delimiter(')> (])operator(,)
+ string<delimiter(')content(Peter Paul Mary)delimiter(')>operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))operator(,)
+ operator([) string<delimiter(')content(To our parents)delimiter(')>operator(,) string<delimiter(')content(Mother Theresa)delimiter(')>operator(,) string<delimiter(')content(God)delimiter(')> (])operator(,)
+ operator([) string<delimiter(')content(pastrami)delimiter(')>operator(,) string<delimiter(')content(ham and cheese)delimiter(')>operator(,) string<delimiter(')content(peanut butter and jelly)delimiter(')>operator(,) string<delimiter(')content(tuna)delimiter(')> (])operator(,)
+ operator([) string<delimiter(')content(recycle tired, old phrases)delimiter(')>operator(,) string<delimiter(')content(ponder big, happy thoughts)delimiter(')> (])operator(,)
+ operator([) string<delimiter(')content(recycle tired, old phrases)delimiter(')>operator(,)
+ string<delimiter(')content(ponder big, happy thoughts)delimiter(')>operator(,)
+ string<delimiter(')content(sleep and dream peacefully)delimiter(')> (])operator(,)
+(])
+
+ident(expected) operator(=) string<delimiter(''')content(
+just one thing
+Mutt and Jeff
+Peter, Paul, and Mary
+To our parents, Mother Theresa, and God
+pastrami, ham and cheese, peanut butter and jelly, and tuna
+recycle tired, old phrases and ponder big, happy thoughts
+recycle tired, old phrases; ponder big, happy thoughts; and sleep and dream peacefully
+)delimiter(''')>
+
+keyword(assert) ident(expected) operator(==) string<delimiter(')content(\\n)delimiter(')> operator(+) ident(lists)operator(.)ident(collect)operator({)ident(commify)operator(()local_variable(it)(\)})operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\)) operator(+) string<delimiter(')content(\\n)delimiter(')>
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// In Groovy, lists and arrays are more or less interchangeable)
+comment(// here is the example using lists)
+ident(people) operator(=) operator([)string<delimiter(')content(Crosby)delimiter(')>operator(,) string<delimiter(')content(Stills)delimiter(')>operator(,) string<delimiter(')content(Nash)delimiter(')>(])
+keyword(assert) ident(people)operator(.)ident(size)operator(()(\)) operator(==) integer(3)
+ident(people)operator([)integer(3)(]) operator(=) string<delimiter(')content(Young)delimiter(')>
+keyword(assert) ident(people)operator(.)ident(size)operator(()(\)) operator(==) integer(4)
+keyword(assert) ident(people) operator(==) operator([)string<delimiter(')content(Crosby)delimiter(')>operator(,) string<delimiter(')content(Stills)delimiter(')>operator(,) string<delimiter(')content(Nash)delimiter(')>operator(,) string<delimiter(')content(Young)delimiter(')>(])
+comment(// to use arrays simply do 'people = peopleArray.toList(\)' at the start)
+comment(// and 'peopleArray = people as String[]' at the end)
+comment(// if you attempt to do extension on a Java array you will get an)
+comment(// ArrayIndexOutOfBoundsException - which is why Java has ArrayList et al)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// list to process)
+ident(people) operator(==) operator([)string<delimiter(')content(Crosby)delimiter(')>operator(,) string<delimiter(')content(Stills)delimiter(')>operator(,) string<delimiter(')content(Nash)delimiter(')>operator(,) string<delimiter(')content(Young)delimiter(')>(])
+comment(// helper)
+ident(startsWithCapital) operator(=) operator({) ident(word) operator(->) ident(word)operator([)integer(0)(]) keyword(in) string<delimiter(')content(A)delimiter(')>operator(..)string<delimiter(')content(Z)delimiter(')> (})
+
+comment(// various styles are possible for processing lists)
+comment(// closure style)
+ident(people)operator(.)ident(each) operator({) ident(person) operator(->) keyword(assert) ident(startsWithCapital)operator(()ident(person)(\)) (})
+comment(// for loop style)
+keyword(for) operator(()ident(person) keyword(in) ident(people)(\)) operator({) keyword(assert) ident(startsWithCapital)operator(()ident(person)(\)) (})
+
+comment(// unixScriptToFindAllUsersStartingWithLetterA:)
+ident(all) operator(=) string<delimiter(')content(who)delimiter(')>operator(.)ident(execute)operator(()(\))operator(.)ident(text)operator(.)ident(replaceAll)operator(()string<delimiter(')content(\\r)delimiter(')>operator(,) string<delimiter(')delimiter(')>(\))operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+ident(all)operator(.)ident(grep)operator(()operator(~)regexp<delimiter(/)content(^a.*)delimiter(/)>(\))operator(.)ident(each)operator({) ident(println) local_variable(it) (})
+
+comment(// printFileWithWordsReversedScript:)
+keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/src/SlowCat.groovy)delimiter(')>(\))operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ ident(line)operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))operator(.)ident(each)operator({) ident(print) local_variable(it)operator(.)ident(reverse)operator(()(\)) (})
+(})
+
+ident(a) operator(=) operator([)float(0.5)operator(,) integer(3)(])operator(;) ident(b) operator(=) operator([)integer(0)operator(,) integer(1)(])
+keyword(assert) operator([)ident(a)operator(,) ident(b)(])operator(.)ident(flatten)operator(()(\))operator(.)ident(collect)operator({) local_variable(it) operator(*) integer(7) (}) operator(==) operator([)float(3.5)operator(,) integer(21)operator(,) integer(0)operator(,) integer(7)(])
+comment(// above doesn't modify original arrays)
+comment(// instead use a = a.collect{ ... })
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// not relevant in Groovy since we have always references)
+ident(items) operator(=) type([])
+keyword(for) operator(()ident(item) keyword(in) ident(items)(\)) operator({)
+ comment(// do something with item)
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.6)
+comment(//----------------------------------------------------------------------------------)
+keyword(assert) operator([) integer(1)operator(,) integer(1)operator(,) integer(2)operator(,) integer(2)operator(,) integer(3)operator(,) integer(3)operator(,) integer(3)operator(,) integer(5) (])operator(.)ident(unique)operator(()(\)) operator(==) operator([) integer(1)operator(,) integer(2)operator(,) integer(3)operator(,) integer(5) (])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.7)
+comment(//----------------------------------------------------------------------------------)
+keyword(assert) operator([) integer(1)operator(,) integer(1)operator(,) integer(2)operator(,) integer(2)operator(,) integer(3)operator(,) integer(3)operator(,) integer(3)operator(,) integer(4)operator(,) integer(5) (]) operator(-) operator([) integer(1)operator(,) integer(2)operator(,) integer(4) (]) operator(==) operator([)integer(3)operator(,) integer(3)operator(,) integer(3)operator(,) integer(5)(])
+keyword(assert) operator([) integer(1)operator(,) integer(1)operator(,) integer(2)operator(,) integer(2)operator(,) integer(3)operator(,) integer(3)operator(,) integer(3)operator(,) integer(4)operator(,) integer(5) (])operator(.)ident(unique)operator(()(\)) operator(-) operator([) integer(1)operator(,) integer(2)operator(,) integer(4) (]) operator(==) operator([)integer(3)operator(,) integer(5)(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.8)
+comment(//----------------------------------------------------------------------------------)
+ident(a) operator(=) operator([)integer(1)operator(,) integer(3)operator(,) integer(5)operator(,) integer(6)operator(,) integer(7)operator(,) integer(8)(])
+ident(b) operator(=) operator([)integer(2)operator(,) integer(3)operator(,) integer(5)operator(,) integer(7)operator(,) integer(9)(])
+comment(// intersection)
+keyword(assert) ident(a)operator(.)ident(intersect)operator(()ident(b)(\)) operator(==) operator([)integer(3)operator(,) integer(5)operator(,) integer(7)(])
+comment(// union)
+keyword(assert) operator(()ident(a) operator(+) ident(b)(\))operator(.)ident(unique)operator(()(\))operator(.)ident(sort)operator(()(\)) operator(==) operator([)integer(1)operator(,) integer(2)operator(,) integer(3)operator(,) integer(5)operator(,) integer(6)operator(,) integer(7)operator(,) integer(8)operator(,) integer(9)(])
+comment(// difference)
+keyword(assert) operator(()ident(a) operator(-) ident(b)(\)) operator(==) operator([)integer(1)operator(,) integer(6)operator(,) integer(8)(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.9)
+comment(//----------------------------------------------------------------------------------)
+ident(members) operator(=) operator([) string<delimiter(")content(Time)delimiter(")>operator(,) string<delimiter(")content(Flies)delimiter(")> (])
+ident(initiates) operator(=) operator([) string<delimiter(")content(An)delimiter(")>operator(,) string<delimiter(")content(Arrow)delimiter(")> (])
+ident(members) operator(+=) ident(initiates)
+keyword(assert) ident(members) operator(==) operator([)string<delimiter(")content(Time)delimiter(")>operator(,) string<delimiter(")content(Flies)delimiter(")>operator(,) string<delimiter(")content(An)delimiter(")>operator(,) string<delimiter(")content(Arrow)delimiter(")>(])
+
+ident(members)operator(.)ident(add)operator(()integer(2)operator(,) string<delimiter(")content(Like)delimiter(")>(\))
+keyword(assert) ident(members) operator(==) operator([)string<delimiter(")content(Time)delimiter(")>operator(,) string<delimiter(")content(Flies)delimiter(")>operator(,) string<delimiter(")content(Like)delimiter(")>operator(,) string<delimiter(")content(An)delimiter(")>operator(,) string<delimiter(")content(Arrow)delimiter(")>(])
+
+ident(members)operator([)integer(0)(]) operator(=) string<delimiter(")content(Fruit)delimiter(")>
+ident(members)operator([)integer(3)operator(..)integer(4)(]) operator(=) operator([)string<delimiter(")content(A)delimiter(")>operator(,) string<delimiter(")content(Banana)delimiter(")>(])
+keyword(assert) ident(members) operator(==) operator([)string<delimiter(")content(Fruit)delimiter(")>operator(,) string<delimiter(")content(Flies)delimiter(")>operator(,) string<delimiter(")content(Like)delimiter(")>operator(,) string<delimiter(")content(A)delimiter(")>operator(,) string<delimiter(")content(Banana)delimiter(")>(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.10)
+comment(//----------------------------------------------------------------------------------)
+ident(items) operator(=) operator([)string<delimiter(")content(the)delimiter(")>operator(,) string<delimiter(")content(quick)delimiter(")>operator(,) string<delimiter(")content(brown)delimiter(")>operator(,) string<delimiter(")content(fox)delimiter(")>(])
+keyword(assert) ident(items)operator(.)ident(reverse)operator(()(\)) operator(==) operator([)string<delimiter(")content(fox)delimiter(")>operator(,) string<delimiter(")content(brown)delimiter(")>operator(,) string<delimiter(")content(quick)delimiter(")>operator(,) string<delimiter(")content(the)delimiter(")>(])
+
+ident(firstLetters) operator(=) type([])
+ident(items)operator(.)ident(reverseEach)operator({) ident(firstLetters) operator(+=) local_variable(it)operator([)integer(0)(]) (})
+keyword(assert) ident(firstLetters)operator(.)ident(join)operator(()(\)) operator(==) string<delimiter(')content(fbqt)delimiter(')>
+
+ident(descending) operator(=) ident(items)operator(.)ident(sort)operator(()(\))operator(.)ident(reverse)operator(()(\))
+keyword(assert) ident(descending) operator(==) operator([)string<delimiter(")content(the)delimiter(")>operator(,) string<delimiter(")content(quick)delimiter(")>operator(,) string<delimiter(")content(fox)delimiter(")>operator(,) string<delimiter(")content(brown)delimiter(")>(])
+ident(descendingBySecondLastLetter) operator(=) ident(items)operator(.)ident(sort) operator({) ident(a)operator(,)ident(b) operator(->) ident(b)operator([)operator(-)integer(2)(]) operator(<=)operator(>) ident(a)operator([)operator(-)integer(2)(]) (})
+keyword(assert) ident(descendingBySecondLastLetter) operator(==) operator([)string<delimiter(")content(brown)delimiter(")>operator(,) string<delimiter(")content(fox)delimiter(")>operator(,) string<delimiter(")content(the)delimiter(")>operator(,) string<delimiter(")content(quick)delimiter(")>(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// warning: not an exact equivalent, idiomatic use would return copies)
+keyword(def) ident(shift2) operator(=) operator({)ident(one) operator(=) ident(friends)operator([)integer(0)(])operator(;) ident(two) operator(=) ident(friends)operator([)integer(1)(])operator(;) integer(2)operator(.)ident(times)operator({)ident(friends)operator(.)ident(remove)operator(()integer(0)(\)}})
+ident(friends) operator(=) string<delimiter(')content(Peter Paul Mary Jim Tim)delimiter(')>operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))operator(.)ident(toList)operator(()(\))
+ident(shift2)operator(()(\))
+keyword(assert) ident(one) operator(==) string<delimiter(')content(Peter)delimiter(')>
+keyword(assert) ident(two) operator(==) string<delimiter(')content(Paul)delimiter(')>
+keyword(assert) ident(friends) operator(==) operator([)string<delimiter(")content(Mary)delimiter(")>operator(,) string<delimiter(")content(Jim)delimiter(")>operator(,) string<delimiter(")content(Tim)delimiter(")>(])
+
+keyword(def) method(pop2)operator(()ident(items)(\)) operator({) ident(items)operator([)integer(0)operator(..)integer(1)(]) (})
+ident(beverages) operator(=) string<delimiter(')content(Dew Jolt Cola Sprite Fresca)delimiter(')>operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))operator(.)ident(toList)operator(()(\))
+ident(pair) operator(=) ident(pop2)operator(()ident(beverages)(\))
+keyword(assert) ident(pair) operator(==) operator([)string<delimiter(")content(Dew)delimiter(")>operator(,) string<delimiter(")content(Jolt)delimiter(")>(])
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_4.12)
+comment(//----------------------------------------------------------------------------------)
+type(class) class(Employee) operator({)
+ keyword(def) ident(name)
+ keyword(def) ident(position)
+ keyword(def) ident(salary)
+(})
+ident(staff) operator(=) operator([)keyword(new) ident(Employee)operator(()key(name)operator(:)string<delimiter(')content(Jim)delimiter(')>operator(,)key(position)operator(:)string<delimiter(')content(Manager)delimiter(')>operator(,)key(salary)operator(:)integer(26000)(\))operator(,)
+ keyword(new) ident(Employee)operator(()key(name)operator(:)string<delimiter(')content(Jill)delimiter(')>operator(,)key(position)operator(:)string<delimiter(')content(Engineer)delimiter(')>operator(,)key(salary)operator(:)integer(24000)(\))operator(,)
+ keyword(new) ident(Employee)operator(()key(name)operator(:)string<delimiter(')content(Jack)delimiter(')>operator(,)key(position)operator(:)string<delimiter(')content(Engineer)delimiter(')>operator(,)key(salary)operator(:)integer(22000)(\)])
+ident(highestEngineer) operator(=) ident(staff)operator(.)ident(find) operator({) ident(emp) operator(->) ident(emp)operator(.)ident(position) operator(==) string<delimiter(')content(Engineer)delimiter(')> (})
+keyword(assert) ident(highestEngineer)operator(.)ident(salary) operator(==) integer(24000)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.13)
+comment(//----------------------------------------------------------------------------------)
+ident(engineers) operator(=) ident(staff)operator(.)ident(findAll) operator({) ident(e) operator(->) ident(e)operator(.)ident(position) operator(==) string<delimiter(')content(Engineer)delimiter(')> (})
+keyword(assert) ident(engineers)operator(.)ident(size)operator(()(\)) operator(==) integer(2)
+
+ident(highPaid) operator(=) ident(staff)operator(.)ident(findAll) operator({) ident(e) operator(->) ident(e)operator(.)ident(salary) operator(>) integer(23000) (})
+keyword(assert) ident(highPaid)operator(*.)ident(name) operator(==) operator([)string<delimiter(")content(Jim)delimiter(")>operator(,) string<delimiter(")content(Jill)delimiter(")>(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.14)
+comment(//----------------------------------------------------------------------------------)
+comment(// sort works for numbers)
+keyword(assert) operator([)integer(100)operator(,) integer(3)operator(,) integer(20)(])operator(.)ident(sort)operator(()(\)) operator(==) operator([)integer(3)operator(,) integer(20)operator(,) integer(100)(])
+comment(// strings representing numbers will be sorted alphabetically)
+keyword(assert) operator([)string<delimiter(')content(100)delimiter(')>operator(,) string<delimiter(')content(3)delimiter(')>operator(,) string<delimiter(')content(20)delimiter(')>(])operator(.)ident(sort)operator(()(\)) operator(==) operator([)string<delimiter(")content(100)delimiter(")>operator(,) string<delimiter(")content(20)delimiter(")>operator(,) string<delimiter(")content(3)delimiter(")>(])
+comment(// closure style sorting allows arbitrary expressions for the comparison)
+keyword(assert) operator([)string<delimiter(')content(100)delimiter(')>operator(,) string<delimiter(')content(3)delimiter(')>operator(,) string<delimiter(')content(20)delimiter(')>(])operator(.)ident(sort)operator({) ident(a)operator(,)ident(b) operator(->) ident(a)operator(.)ident(toLong)operator(()(\)) operator(<=)operator(>) ident(b)operator(.)ident(toLong)operator(()(\)}) operator(==) operator([)string<delimiter(")content(3)delimiter(")>operator(,) string<delimiter(")content(20)delimiter(")>operator(,) string<delimiter(")content(100)delimiter(")>(])
+
+comment(// obtain the following on unix systems using: 'ps ux'.execute(\).text)
+ident(processInput) operator(=) string<delimiter(''')content(
+ PID PPID PGID WINPID TTY UID STIME COMMAND
+ 3868 1 3868 3868 con 1005 06:23:34 /usr/bin/bash
+ 3456 3868 3456 3528 con 1005 06:23:39 /usr/bin/ps
+)delimiter(''')>
+ident(nonEmptyLines) operator(=) operator({)local_variable(it)operator(.)ident(trim)operator(()(\)})
+ident(lines) operator(=) ident(processInput)operator(.)ident(split)operator(()string<delimiter(")char(\\n)delimiter(")>(\))operator(.)ident(findAll)operator(()ident(nonEmptyLines)(\))operator([)integer(1)operator(..)operator(-)integer(1)(])
+keyword(def) method(col)operator(()ident(n)operator(,) ident(s)(\)) operator({) ident(s)operator(.)ident(tokenize)operator(()(\))operator([)ident(n)(]) (})
+ident(commandIdx) operator(=) integer(7)
+ident(pidIdx) operator(=) integer(0)
+ident(ppidIdx) operator(=) integer(1)
+ident(linesByPid) operator(=) ident(lines)operator(.)ident(sort)operator({) ident(col)operator(()ident(pidIdx)operator(,)local_variable(it)(\))operator(.)ident(toLong)operator(()(\)) (})
+keyword(assert) ident(col)operator(()ident(commandIdx)operator(,) ident(linesByPid)operator([)integer(0)(]\)) operator(==) string<delimiter(')content(/usr/bin/ps)delimiter(')>
+ident(linesByPpid) operator(=) ident(lines)operator(.)ident(sort)operator({) ident(col)operator(()ident(ppidIdx)operator(,)local_variable(it)(\))operator(.)ident(toLong)operator(()(\)) (})
+keyword(assert) ident(col)operator(()ident(commandIdx)operator(,) ident(linesByPpid)operator([)integer(0)(]\)) operator(==) string<delimiter(')content(/usr/bin/bash)delimiter(')>
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.15)
+comment(//----------------------------------------------------------------------------------)
+comment(// sort staff from 4.12 by name)
+keyword(assert) ident(staff)operator(.)ident(sort) operator({) ident(a)operator(,)ident(b) operator(->) ident(a)operator(.)ident(name) operator(<=)operator(>) ident(b)operator(.)ident(name) (})operator(*.)ident(name) operator(==) operator([)string<delimiter(")content(Jack)delimiter(")>operator(,) string<delimiter(")content(Jill)delimiter(")>operator(,) string<delimiter(")content(Jim)delimiter(")>(])
+comment(// sort by first two characters of name and if equal by descending salary)
+keyword(assert) ident(staff)operator(.)ident(sort) operator({) ident(a)operator(,)ident(b) operator(->)
+ ident(astart) operator(=) ident(a)operator(.)ident(name)operator([)integer(0)operator(..)integer(1)(])
+ ident(bstart) operator(=) ident(b)operator(.)ident(name)operator([)integer(0)operator(..)integer(1)(])
+ keyword(if) operator(()ident(astart) operator(==) ident(bstart)(\)) keyword(return) ident(b)operator(.)ident(salary) operator(<=)operator(>) ident(a)operator(.)ident(salary)
+ keyword(return) ident(astart) operator(<=)operator(>) ident(bstart)
+(})operator(*.)ident(name) operator(==) operator([)string<delimiter(")content(Jack)delimiter(")>operator(,) string<delimiter(")content(Jim)delimiter(")>operator(,) string<delimiter(")content(Jill)delimiter(")>(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.16)
+comment(//----------------------------------------------------------------------------------)
+ident(items) operator(=) operator([)integer(1)operator(,) integer(2)operator(,) integer(3)operator(,) integer(4)operator(,) integer(5)(])
+ident(processed) operator(=) type([])
+integer(10)operator(.)ident(times)operator({)
+ ident(processed) operator(<)operator(<) ident(items)operator([)integer(0)(])
+ ident(items) operator(=) ident(items)operator([)integer(1)operator(..)operator(-)integer(1)(]) operator(+) ident(items)operator([)integer(0)(])
+(})
+keyword(assert) ident(processed) operator(==) operator([)integer(1)operator(,) integer(2)operator(,) integer(3)operator(,) integer(4)operator(,) integer(5)operator(,) integer(1)operator(,) integer(2)operator(,) integer(3)operator(,) integer(4)operator(,) integer(5)(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.17)
+comment(//----------------------------------------------------------------------------------)
+keyword(import) include(java.text.DateFormatSymbols) keyword(as) class(Symbols)
+ident(items) operator(=) keyword(new) ident(Symbols)operator(()(\))operator(.)ident(shortWeekdays)operator(.)ident(toList)operator(()(\))operator([)integer(1)operator(..)integer(7)(])
+keyword(assert) ident(items) operator(==) operator([)string<delimiter(")content(Sun)delimiter(")>operator(,) string<delimiter(")content(Mon)delimiter(")>operator(,) string<delimiter(")content(Tue)delimiter(")>operator(,) string<delimiter(")content(Wed)delimiter(")>operator(,) string<delimiter(")content(Thu)delimiter(")>operator(,) string<delimiter(")content(Fri)delimiter(")>operator(,) string<delimiter(")content(Sat)delimiter(")>(])
+comment(// not as random as you might expect)
+ident(println) ident(items)operator(.)ident(sort)operator({) pre_type(Math)operator(.)ident(random)operator(()(\)) (})
+comment(// => ["Sat", "Tue", "Sun", "Wed", "Mon", "Thu", "Fri"])
+comment(// better to use the built-in method for this purpose)
+pre_type(Collections)operator(.)ident(shuffle)operator(()ident(items)(\))
+ident(println) ident(items)
+comment(// => ["Wed", "Tue", "Fri", "Sun", "Sat", "Thu", "Mon"])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.18)
+comment(//----------------------------------------------------------------------------------)
+ident(symbols) operator(=) keyword(new) ident(Symbols)operator(()(\))
+ident(words) operator(=) ident(symbols)operator(.)ident(weekdays)operator(.)ident(toList)operator(()(\))operator([)integer(1)operator(..)integer(7)(]) operator(+)
+ ident(symbols)operator(.)ident(months)operator(.)ident(toList)operator(()(\))operator([)integer(0)operator(..)integer(11)(]) operator(+)
+ ident(symbols)operator(.)ident(eras)operator(.)ident(toList)operator(()(\)) operator(+)
+ ident(symbols)operator(.)ident(amPmStrings)operator(.)ident(toList)operator(()(\))
+
+ident(expected) operator(=) comment(//)
+string<delimiter(')content(AD August February July May October September Tuesday )content(\\n)delimiter(')> operator(+)
+string<delimiter(')content(AM BC Friday June Monday PM Sunday Wednesday )content(\\n)delimiter(')> operator(+)
+string<delimiter(')content(April December January March November Saturday Thursday )content(\\n)delimiter(')>
+
+type(class) class(WordFormatter) operator({)
+ keyword(def) ident(cols)
+
+ keyword(def) method(process)operator(()ident(list)(\)) operator({)
+ keyword(def) ident(sb) operator(=) keyword(new) pre_type(StringBuffer)operator(()(\))
+ keyword(def) ident(colWidth) operator(=) ident(list)operator(.)ident(max)operator({)local_variable(it)operator(.)ident(size)operator(()(\)})operator(.)ident(size)operator(()(\)) operator(+) integer(1)
+ type(int) ident(columns) operator(=) operator([)ident(cols)operator(/)ident(colWidth)operator(,) integer(1)(])operator(.)ident(max)operator(()(\))
+ keyword(def) ident(numWords) operator(=) ident(list)operator(.)ident(size)operator(()(\))
+ type(int) ident(rows) operator(=) operator(()ident(numWords) operator(+) ident(columns) operator(-) integer(1)(\)) operator(/) ident(columns)
+ keyword(for) operator(()ident(row) keyword(in) integer(0)operator(..<)ident(rows)(\)) operator({)
+ keyword(for) operator(()ident(col) keyword(in) integer(0)operator(..<)ident(columns)(\)) operator({)
+ keyword(def) ident(target) operator(=) ident(row) operator(+) ident(col) operator(*) ident(rows)
+ keyword(if) operator(()ident(target) operator(<) ident(numWords)(\))
+ ident(sb) operator(<)operator(<) ident(list)operator([)ident(target)(])operator(.)ident(padRight)operator(()ident(colWidth)(\))
+ (})
+ ident(sb) operator(<)operator(<) string<delimiter(')content(\\n)delimiter(')>
+ (})
+ keyword(return) ident(sb)operator(.)ident(toString)operator(()(\))
+ (})
+(})
+
+comment(// get nr of chars that fit in window or console, see PLEAC 15.4)
+comment(// hard-coded here but several packages are available, e.g. in JLine)
+comment(// use a concrete implementation of Terminal.getTerminalWidth(\))
+keyword(def) method(getWinCharWidth)operator(()(\)) operator({) integer(80) (})
+
+comment(// main script)
+ident(actual) operator(=) keyword(new) ident(WordFormatter)operator(()key(cols)operator(:)ident(getWinCharWidth)operator(()(\)\))operator(.)ident(process)operator(()ident(words)operator(.)ident(sort)operator(()(\)\))
+keyword(assert) ident(actual) operator(==) ident(expected)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_4.19)
+comment(//----------------------------------------------------------------------------------)
+comment(// recursive version is simplest but can be inefficient)
+keyword(def) method(fact)operator(()ident(n)(\)) operator({) operator(()ident(n) operator(==) integer(1)(\)) operator(?) integer(1) operator(:) ident(n) operator(*) ident(fact)operator(()ident(n)operator(-)integer(1)(\)})
+keyword(assert) ident(fact)operator(()integer(10)(\)) operator(==) integer(3628800)
+comment(// unwrapped version: note use of BigInteger)
+keyword(def) method(factorial)operator(()ident(n)(\)) operator({)
+ keyword(def) ident(result) operator(=) integer(1G) comment(// 1 as BigInteger)
+ keyword(while) operator(()ident(n) operator(>) integer(0)(\)) operator({)
+ ident(result) operator(*=) ident(n)
+ ident(n) operator(-=) integer(1)
+ (})
+ keyword(return) ident(result)
+(})
+ident(expected) operator(=) integer(93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000)
+keyword(assert) ident(expected) operator(==) ident(factorial)operator(()integer(100)(\))
+comment(// println factorial(10000\))
+comment(// => 284625... (greater than 35,000 digits\))
+
+comment(// simple version but less efficient)
+keyword(def) method(simplePermute)operator(()ident(items)operator(,) ident(perms)(\)) operator({)
+ keyword(if) operator(()ident(items)operator(.)ident(size)operator(()(\)) operator(==) integer(0)(\))
+ ident(println) ident(perms)operator(.)ident(join)operator(()string<delimiter(')content( )delimiter(')>(\))
+ keyword(else)
+ keyword(for) operator(()ident(i) keyword(in) ident(items)(\)) operator({)
+ ident(newitems) operator(=) ident(items)operator(.)ident(clone)operator(()(\))
+ ident(newperms) operator(=) ident(perms)operator(.)ident(clone)operator(()(\))
+ ident(newperms)operator(.)ident(add)operator(()ident(i)(\))
+ ident(newitems)operator(.)ident(remove)operator(()ident(i)(\))
+ ident(simplePermute)operator(()ident(newitems)operator(,) ident(newperms)(\))
+ (})
+(})
+ident(simplePermute)operator(()operator([)string<delimiter(')content(dog)delimiter(')>operator(,) string<delimiter(')content(bites)delimiter(')>operator(,) string<delimiter(')content(man)delimiter(')>(])operator(,) type([])(\))
+comment(// =>)
+comment(//dog bites man)
+comment(//dog man bites)
+comment(//bites dog man)
+comment(//bites man dog)
+comment(//man dog bites)
+comment(//man bites dog)
+
+comment(// optimised version below)
+ident(expected) operator(=) string<delimiter(''')content(
+man bites dog
+man dog bites
+bites man dog
+bites dog man
+dog man bites
+dog bites man
+)delimiter(''')>
+
+comment(// n2pat(n, len\): produce the N-th pattern of length len)
+keyword(def) method(n2pat)operator(()ident(n)operator(,) ident(length)(\)) operator({)
+ keyword(def) ident(pat) operator(=) type([])
+ type(int) ident(i) operator(=) integer(1)
+ keyword(while) operator(()ident(i) operator(<=) ident(length)(\)) operator({)
+ ident(pat) operator(<)operator(<) operator(()ident(n) operator(%) ident(i)(\))
+ ident(n) operator(=) ident(n)operator(.)ident(intdiv)operator(()ident(i)(\))
+ ident(i) operator(+=) integer(1)
+ (})
+ ident(pat)
+(})
+
+comment(// pat2perm(pat\): turn pattern returned by n2pat(\) into)
+comment(// permutation of integers.)
+keyword(def) method(pat2perm)operator(()ident(pat)(\)) operator({)
+ keyword(def) ident(source) operator(=) operator(()integer(0) operator(..<) ident(pat)operator(.)ident(size)operator(()(\)\))operator(.)ident(collect)operator({) local_variable(it)comment(/*.toString(\)*/) (})
+ keyword(def) ident(perm) operator(=) type([])
+ keyword(while) operator(()ident(pat)operator(.)ident(size)operator(()(\)) operator(>) integer(0)(\)) operator({)
+ keyword(def) ident(next) operator(=) ident(pat)operator(.)ident(remove)operator(()ident(pat)operator(.)ident(size)operator(()(\))operator(-)integer(1)(\))
+ ident(perm) operator(<)operator(<) ident(source)operator([)ident(next)(])
+ ident(source)operator(.)ident(remove)operator(()ident(next)(\))
+ (})
+ ident(perm)
+(})
+
+keyword(def) method(n2perm)operator(()ident(n)operator(,) ident(len)(\)) operator({)
+ ident(pat2perm)operator(()ident(n2pat)operator(()operator(()type(int)(\))ident(n)operator(,)ident(len)(\)\))
+(})
+
+ident(data) operator(=) operator([)string<delimiter(')content(man)delimiter(')>operator(,) string<delimiter(')content(bites)delimiter(')>operator(,) string<delimiter(')content(dog)delimiter(')>(])
+ident(sb) operator(=) keyword(new) pre_type(StringBuffer)operator(()(\))
+ident(numPermutations) operator(=) ident(fact)operator(()ident(data)operator(.)ident(size)operator(()(\)\))
+keyword(for) operator(()ident(j) keyword(in) integer(0)operator(..<)ident(numPermutations)(\)) operator({)
+ keyword(def) ident(permutation) operator(=) ident(n2perm)operator(()ident(j)operator(,) ident(data)operator(.)ident(size)operator(()(\)\))operator(.)ident(collect) operator({) ident(k) operator(->) ident(data)operator([)ident(k)(]) (})
+ ident(sb) operator(<)operator(<) ident(permutation)operator(.)ident(join)operator(()string<delimiter(')content( )delimiter(')>(\)) operator(+) string<delimiter(')content(\\n)delimiter(')>
+(})
+keyword(assert) string<delimiter(')content(\\n)delimiter(')> operator(+) ident(sb)operator(.)ident(toString)operator(()(\)) operator(==) ident(expected)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.0)
+comment(//----------------------------------------------------------------------------------)
+comment(// quotes are optional around the key)
+ident(age) operator(=) operator([) key(Nat)operator(:)integer(24)operator(,) key(Jules)operator(:)integer(25)operator(,) key(Josh)operator(:)integer(17) (])
+
+keyword(assert) ident(age)operator([)string<delimiter(')content(Nat)delimiter(')>(]) operator(==) integer(24)
+comment(// alternate syntax)
+keyword(assert) ident(age)operator(.)string<delimiter(")content(Jules)delimiter(")> operator(==) integer(25)
+
+ident(foodColor) operator(=) operator([)
+ key(Apple)operator(:) string<delimiter(')content(red)delimiter(')>operator(,)
+ key(Banana)operator(:) string<delimiter(')content(yellow)delimiter(')>operator(,)
+ key(Lemon)operator(:) string<delimiter(')content(yellow)delimiter(')>operator(,)
+ key(Carrot)operator(:) string<delimiter(')content(orange)delimiter(')>
+(])
+keyword(assert) ident(foodColor)operator(.)ident(size)operator(()(\)) operator(==) integer(4)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.1)
+comment(//----------------------------------------------------------------------------------)
+ident(foodColor)operator([)string<delimiter(')content(Lemon)delimiter(')>(]) operator(=) string<delimiter(')content(green)delimiter(')>
+keyword(assert) ident(foodColor)operator(.)ident(size)operator(()(\)) operator(==) integer(4)
+keyword(assert) ident(foodColor)operator([)string<delimiter(')content(Lemon)delimiter(')>(]) operator(==) string<delimiter(')content(green)delimiter(')>
+ident(foodColor)operator([)string<delimiter(')content(Raspberry)delimiter(')>(]) operator(=) string<delimiter(')content(pink)delimiter(')>
+keyword(assert) ident(foodColor)operator(.)ident(size)operator(()(\)) operator(==) integer(5)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.2)
+comment(//----------------------------------------------------------------------------------)
+keyword(assert) operator([)string<delimiter(')content(Banana)delimiter(')>operator(,) string<delimiter(')content(Martini)delimiter(')>(])operator(.)ident(collect)operator({) ident(foodColor)operator(.)ident(containsKey)operator(()local_variable(it)(\))operator(?)string<delimiter(')content(food)delimiter(')>operator(:)string<delimiter(')content(drink)delimiter(')> (}) operator(==) operator([) string<delimiter(')content(food)delimiter(')>operator(,) string<delimiter(')content(drink)delimiter(')> (])
+
+ident(age) operator(=) operator([)key(Toddler)operator(:)integer(3)operator(,) key(Unborn)operator(:)integer(0)operator(,) key(Phantasm)operator(:)keyword(null)(])
+operator([)string<delimiter(')content(Toddler)delimiter(')>operator(,) string<delimiter(')content(Unborn)delimiter(')>operator(,) string<delimiter(')content(Phantasm)delimiter(')>operator(,) string<delimiter(')content(Relic)delimiter(')>(])operator(.)ident(each)operator({) ident(key) operator(->)
+ ident(print) string<delimiter(")inline<inline_delimiter($)ident(key)>content(: )delimiter(")>
+ keyword(if) operator(()ident(age)operator(.)ident(containsKey)operator(()ident(key)(\)\)) ident(print) string<delimiter(')content(has key )delimiter(')>
+ keyword(if) operator(()ident(age)operator(.)ident(containsKey)operator(()ident(key)(\)) operator(&&) ident(age)operator([)ident(key)(])operator(!=)keyword(null)(\)) ident(print) string<delimiter(')content(non-null )delimiter(')>
+ keyword(if) operator(()ident(age)operator(.)ident(containsKey)operator(()ident(key)(\)) operator(&&) ident(age)operator([)ident(key)(]\)) ident(print) string<delimiter(')content(true )delimiter(')>
+ ident(println) string<delimiter(')delimiter(')>
+(})
+comment(// =>)
+comment(// Toddler: has key non-null true)
+comment(// Unborn: has key non-null)
+comment(// Phantasm: has key)
+comment(// Relic:)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.3)
+comment(//----------------------------------------------------------------------------------)
+keyword(assert) ident(foodColor)operator(.)ident(size)operator(()(\)) operator(==) integer(5)
+ident(foodColor)operator(.)ident(remove)operator(()string<delimiter(')content(Banana)delimiter(')>(\))
+keyword(assert) ident(foodColor)operator(.)ident(size)operator(()(\)) operator(==) integer(4)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.4)
+comment(//----------------------------------------------------------------------------------)
+ident(hash) operator(=) operator([)operator(:)(])
+ident(hash)operator(.)ident(each) operator({) ident(key)operator(,) ident(value) operator(->)
+ comment(// do something with key and value)
+(})
+
+ident(hash)operator(.)ident(each) operator({) ident(entry) operator(->)
+ comment(// do something with entry)
+(})
+
+ident(hash)operator(.)ident(keySet)operator(()(\))operator(.)ident(each) operator({) ident(key) operator(->)
+ comment(// do something with key)
+(})
+
+ident(sb) operator(=) keyword(new) pre_type(StringBuffer)operator(()(\))
+ident(foodColor)operator(.)ident(each) operator({) ident(food)operator(,) ident(color) operator(->)
+ ident(sb) operator(<)operator(<) string<delimiter(")inline<inline_delimiter($)ident(food)>content( is )inline<inline_delimiter($)ident(color)>char(\\n)delimiter(")>
+(})
+keyword(assert) string<delimiter(')content(\\n)delimiter(')> operator(+) ident(sb)operator(.)ident(toString)operator(()(\)) operator(==) string<delimiter(''')content(
+Lemon is green
+Carrot is orange
+Apple is red
+Raspberry is pink
+)delimiter(''')>
+
+ident(foodColor)operator(.)ident(each) operator({) ident(entry) operator(->)
+ keyword(assert) ident(entry)operator(.)ident(key)operator(.)ident(size)operator(()(\)) operator(>) integer(4) operator(&&) ident(entry)operator(.)ident(value)operator(.)ident(size)operator(()(\)) operator(>) integer(2)
+(})
+
+ident(foodColorsSortedByFood) operator(=) type([])
+ident(foodColor)operator(.)ident(keySet)operator(()(\))operator(.)ident(sort)operator(()(\))operator(.)ident(each) operator({) ident(k) operator(->) ident(foodColorsSortedByFood) operator(<)operator(<) ident(foodColor)operator([)ident(k)(]) (})
+keyword(assert) ident(foodColorsSortedByFood) operator(==) operator([)string<delimiter(")content(red)delimiter(")>operator(,) string<delimiter(")content(orange)delimiter(")>operator(,) string<delimiter(")content(green)delimiter(")>operator(,) string<delimiter(")content(pink)delimiter(")>(])
+
+ident(fakedInput) operator(=) string<delimiter(''')content(
+From: someone@somewhere.com
+From: someone@spam.com
+From: someone@somewhere.com
+)delimiter(''')>
+
+ident(from) operator(=) operator([)operator(:)(])
+ident(fakedInput)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(each)operator({)
+ ident(matcher) operator(=) operator(()local_variable(it) operator(=~) regexp<delimiter(/)content(^From:)char(\\s)content(+([^)char(\\s)content(>]*\))delimiter(/)>(\))
+ keyword(if) operator(()ident(matcher)operator(.)ident(matches)operator(()(\)\)) operator({)
+ ident(sender) operator(=) ident(matcher)operator([)integer(0)(])operator([)integer(1)(])
+ keyword(if) operator(()ident(from)operator(.)ident(containsKey)operator(()ident(sender)(\)\)) ident(from)operator([)ident(sender)(]) operator(+=) integer(1)
+ keyword(else) ident(from)operator([)ident(sender)(]) operator(=) integer(1)
+ (})
+(})
+
+comment(// More useful to sort by number of received mail by person)
+ident(from)operator(.)ident(entrySet)operator(()(\))operator(.)ident(sort) operator({) ident(a)operator(,)ident(b) operator(->) ident(b)operator(.)ident(value)operator(<=)operator(>)ident(a)operator(.)ident(value)(})operator(.)ident(each) operator({) ident(e)operator(->)
+ ident(println) string<delimiter(")inline<inline_delimiter(${)ident(e.key)inline_delimiter(})>content(: )inline<inline_delimiter(${)ident(e.value)inline_delimiter(})>delimiter(")>
+(})
+comment(// =>)
+comment(// someone@somewhere.com: 2)
+comment(// someone@spam.com: 1)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.5)
+comment(//----------------------------------------------------------------------------------)
+ident(hash) operator(=) operator([)key(a)operator(:)integer(1)operator(,) key(b)operator(:)integer(2)operator(,) key(c)operator(:)integer(3)(])
+comment(// Map#toString already produce a pretty decent output:)
+ident(println) ident(hash)
+comment(// => ["b":2, "a":1, "c":3])
+
+comment(// Or do it by longhand for customised formatting)
+ident(hash)operator(.)ident(each) operator({) ident(k)operator(,)ident(v) operator(->) ident(println) string<delimiter(")inline<inline_delimiter($)ident(k)>content( => )inline<inline_delimiter($)ident(v)>delimiter(")> (})
+comment(// =>)
+comment(// b => 2)
+comment(// a => 1)
+comment(// c => 3)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// java.util.LinkedHashMap "maintains a doubly-linked list running through all of its entries.)
+comment(// This linked list defines the iteration ordering, which is normally the order in which keys)
+comment(// were inserted into the map (insertion-order\)".)
+ident(foodColor) operator(=) keyword(new) pre_type(LinkedHashMap)operator(()(\))
+ident(foodColor)operator([)string<delimiter(')content(Banana)delimiter(')>(]) operator(=) string<delimiter(')content(Yellow)delimiter(')>
+ident(foodColor)operator([)string<delimiter(')content(Apple)delimiter(')>(]) operator(=) string<delimiter(')content(Green)delimiter(')>
+ident(foodColor)operator([)string<delimiter(')content(Lemon)delimiter(')>(]) operator(=) string<delimiter(')content(Yellow)delimiter(')>
+
+ident(foodColor)operator(.)ident(keySet)operator(()(\))operator(.)ident(each)operator({) ident(key) operator(->) ident(println) ident(key) (})
+comment(// =>)
+comment(// Banana)
+comment(// Apple)
+comment(// Lemon)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.7)
+comment(//----------------------------------------------------------------------------------)
+ident(foodsOfColor) operator(=) operator([) key(Yellow)operator(:)operator([)string<delimiter(')content(Banana)delimiter(')>operator(,) string<delimiter(')content(Lemon)delimiter(')>(])operator(,) key(Green)operator(:)operator([)string<delimiter(')content(Apple)delimiter(')>(]) (])
+ident(foodsOfColor)operator([)string<delimiter(')content(Green)delimiter(')>(]) operator(+=) string<delimiter(')content(Melon)delimiter(')>
+keyword(assert) ident(foodsOfColor) operator(==) operator([)string<delimiter(")content(Green)delimiter(")>operator(:)operator([)string<delimiter(")content(Apple)delimiter(")>operator(,) string<delimiter(")content(Melon)delimiter(")>(])operator(,) string<delimiter(")content(Yellow)delimiter(")>operator(:)operator([)string<delimiter(")content(Banana)delimiter(")>operator(,) string<delimiter(")content(Lemon)delimiter(")>(]])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.8)
+comment(//----------------------------------------------------------------------------------)
+ident(surname) operator(=) operator([)key(Mickey)operator(:) string<delimiter(')content(Mantle)delimiter(')>operator(,) key(Babe)operator(:) string<delimiter(')content(Ruth)delimiter(')>(])
+keyword(assert) ident(surname)operator(.)ident(findAll)operator({) local_variable(it)operator(.)ident(value) operator(==) string<delimiter(')content(Mantle)delimiter(')> (})operator(.)ident(collect)operator({) local_variable(it)operator(.)ident(key) (}) operator(==) operator([)string<delimiter(")content(Mickey)delimiter(")>(])
+
+ident(firstname) operator(=) operator([)operator(:)(])
+ident(surname)operator(.)ident(each)operator({) ident(entry) operator(->) ident(firstname)operator([)ident(entry)operator(.)ident(value)(]) operator(=) ident(entry)operator(.)ident(key) (})
+keyword(assert) ident(firstname) operator(==) operator([)string<delimiter(")content(Ruth)delimiter(")>operator(:)string<delimiter(")content(Babe)delimiter(")>operator(,) string<delimiter(")content(Mantle)delimiter(")>operator(:)string<delimiter(")content(Mickey)delimiter(")>(])
+
+comment(// foodfindScript:)
+doctype(#!/usr/bin/groovy)
+comment(// usage: foodfind food_or_color")
+ident(color) operator(=) operator([)key(Apple)operator(:)string<delimiter(')content(red)delimiter(')>operator(,) key(Banana)operator(:)string<delimiter(')content(yellow)delimiter(')>operator(,) key(Lemon)operator(:)string<delimiter(')content(yellow)delimiter(')>operator(,) key(Carrot)operator(:)string<delimiter(')content(orange)delimiter(')>(])
+ident(given) operator(=) ident(args)operator([)integer(0)(])
+keyword(if) operator(()ident(color)operator(.)ident(containsKey)operator(()ident(given)(\)\))
+ ident(println) string<delimiter(")inline<inline_delimiter($)ident(given)>content( is a food with color )inline<inline_delimiter(${)ident(color[given])inline_delimiter(})>content(.)delimiter(")>
+ident(if) operator(()ident(color)operator(.)ident(containsValue)operator(()ident(given)(\)\)) operator({)
+ comment(// could use commify(\) here - see 4.2)
+ ident(foods) operator(=) ident(color)operator(.)ident(findAll)operator({)local_variable(it)operator(.)ident(value) operator(==) ident(given)(})operator(.)ident(collect)operator({)local_variable(it)operator(.)ident(key)(})
+ ident(join) operator(=) ident(foods)operator(.)ident(size)operator(()(\)) operator(==) integer(1) operator(?) string<delimiter(')content(is a food)delimiter(')> operator(:) string<delimiter(')content(are foods)delimiter(')>
+ ident(println) string<delimiter(")inline<inline_delimiter(${)ident(foods.join(', '\))inline_delimiter(})>content( )inline<inline_delimiter($)ident(join)>content( with color )inline<inline_delimiter(${)ident(given)inline_delimiter(})>content(.)delimiter(")>
+(})
+comment(// foodfind red)
+comment(// => Apple is a food with color red.)
+comment(// foodfind yellow)
+comment(// => Lemon, Banana are foods with color yellow.)
+comment(// foodfind Carrot)
+comment(// => Carrot is a food with color orange.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.9)
+comment(//----------------------------------------------------------------------------------)
+ident(foodColor) operator(=) operator([)key(Apple)operator(:)string<delimiter(')content(red)delimiter(')>operator(,) key(Carrot)operator(:)string<delimiter(')content(orange)delimiter(')>operator(,) key(Banana)operator(:)string<delimiter(')content(yellow)delimiter(')>operator(,) key(Cherry)operator(:)string<delimiter(')content(black)delimiter(')>(])
+
+comment(// Sorted by keys)
+keyword(assert) ident(foodColor)operator(.)ident(keySet)operator(()(\))operator(.)ident(sort)operator(()(\)) operator(==) operator([)string<delimiter(")content(Apple)delimiter(")>operator(,) string<delimiter(")content(Banana)delimiter(")>operator(,) string<delimiter(")content(Carrot)delimiter(")>operator(,) string<delimiter(")content(Cherry)delimiter(")>(])
+comment(// you could now iterate through the hash with the sorted keys)
+keyword(assert) ident(foodColor)operator(.)ident(values)operator(()(\))operator(.)ident(sort)operator(()(\)) operator(==) operator([)string<delimiter(")content(black)delimiter(")>operator(,) string<delimiter(")content(orange)delimiter(")>operator(,) string<delimiter(")content(red)delimiter(")>operator(,) string<delimiter(")content(yellow)delimiter(")>(])
+keyword(assert) ident(foodColor)operator(.)ident(values)operator(()(\))operator(.)ident(sort)operator({)local_variable(it)operator(.)ident(size)operator(()(\)}) operator(==) operator([)string<delimiter(")content(red)delimiter(")>operator(,) string<delimiter(")content(black)delimiter(")>operator(,) string<delimiter(")content(orange)delimiter(")>operator(,) string<delimiter(")content(yellow)delimiter(")>(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.10)
+comment(//----------------------------------------------------------------------------------)
+comment(//merged = a.clone.update(b\) # because Hash#update changes object in place)
+
+ident(drinkColor) operator(=) operator([)key(Galliano)operator(:)string<delimiter(')content(yellow)delimiter(')>operator(,) string<delimiter(')content(Mai Tai)delimiter(')>operator(:)string<delimiter(')content(blue)delimiter(')>(])
+ident(ingestedColor) operator(=) operator([)operator(:)(])
+ident(ingestedColor)operator(.)ident(putAll)operator(()ident(drinkColor)(\))
+comment(// overrides any common keys)
+ident(ingestedColor)operator(.)ident(putAll)operator(()ident(foodColor)(\))
+
+ident(totalColors) operator(=) ident(ingestedColor)operator(.)ident(values)operator(()(\))operator(.)ident(sort)operator(()(\))operator(.)ident(unique)operator(()(\))
+keyword(assert) ident(totalColors) operator(==) operator([)string<delimiter(")content(black)delimiter(")>operator(,) string<delimiter(")content(blue)delimiter(")>operator(,) string<delimiter(")content(orange)delimiter(")>operator(,) string<delimiter(")content(red)delimiter(")>operator(,) string<delimiter(")content(yellow)delimiter(")>(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.11)
+comment(//----------------------------------------------------------------------------------)
+ident(foodColor)operator([)string<delimiter(')content(Lemon)delimiter(')>(])operator(=)string<delimiter(')content(yellow)delimiter(')>
+ident(citrusColor) operator(=) operator([)key(Lemon)operator(:)string<delimiter(')content(yellow)delimiter(')>operator(,) key(Orange)operator(:)string<delimiter(')content(orange)delimiter(')>operator(,) key(Lime)operator(:)string<delimiter(')content(green)delimiter(')>(])
+ident(println) ident(foodColor)
+ident(println) ident(citrusColor)
+ident(common) operator(=) ident(foodColor)operator(.)ident(keySet)operator(()(\))operator(.)ident(intersect)operator(()ident(citrusColor)operator(.)ident(keySet)operator(()(\)\))
+keyword(assert) ident(common) operator(==) operator([)string<delimiter(")content(Lemon)delimiter(")>(])
+
+ident(foodButNotCitrus) operator(=) ident(foodColor)operator(.)ident(keySet)operator(()(\))operator(.)ident(toList)operator(()(\)) operator(-) ident(citrusColor)operator(.)ident(keySet)operator(()(\))operator(.)ident(toList)operator(()(\))
+keyword(assert) ident(foodButNotCitrus) operator(==) operator([)string<delimiter(")content(Carrot)delimiter(")>operator(,) string<delimiter(")content(Apple)delimiter(")>operator(,) string<delimiter(")content(Banana)delimiter(")>operator(,) string<delimiter(")content(Cherry)delimiter(")>(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.12)
+comment(//----------------------------------------------------------------------------------)
+comment(// no problem here, Groovy handles any kind of object for key-ing)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy uses Java implementations for storing hashes and these)
+comment(// support setting an initial capacity and load factor (which determines)
+comment(// at what point the hash will be resized if needed\))
+ident(hash) operator(=) operator([)operator(:)(]) comment(// Groovy shorthand gets defaults)
+ident(hash) operator(=) keyword(new) pre_type(HashMap)operator(()(\)) comment(// default capacity and load factor)
+ident(println) ident(hash)operator(.)ident(capacity)operator(()(\))
+comment(// => 16)
+operator(()string<delimiter(')content(A)delimiter(')>operator(..)string<delimiter(')content(Z)delimiter(')>(\))operator(.)ident(each)operator({) ident(hash)operator([)local_variable(it)(]) operator(=) local_variable(it) (})
+ident(println) ident(hash)operator(.)ident(capacity)operator(()(\))
+comment(// => 64)
+ident(hash) operator(=) keyword(new) pre_type(HashMap)operator(()integer(100)(\)) comment(// initial capacity of 100 and default load factor)
+ident(hash) operator(=) keyword(new) pre_type(HashMap)operator(()integer(100)operator(,) float(0.8f)(\)) comment(// initial capacity of 100 and 0.8 load factor)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.14)
+comment(//----------------------------------------------------------------------------------)
+ident(count) operator(=) operator([)operator(:)(])
+ident(letters) operator(=) type([])
+ident(foodColor)operator(.)ident(values)operator(()(\))operator(.)ident(each)operator({) ident(letters)operator(.)ident(addAll)operator(()operator(()local_variable(it) keyword(as) pre_type(String)type([])(\))operator(.)ident(toList)operator(()(\)\)) (})
+ident(letters)operator(.)ident(each)operator({) keyword(if) operator(()ident(count)operator(.)ident(containsKey)operator(()local_variable(it)(\)\)) ident(count)operator([)local_variable(it)(]) operator(+=) integer(1) keyword(else) ident(count)operator([)local_variable(it)(]) operator(=) integer(1) (})
+keyword(assert) ident(count) operator(==) operator([)string<delimiter(")content(o)delimiter(")>operator(:)integer(3)operator(,) string<delimiter(")content(d)delimiter(")>operator(:)integer(1)operator(,) string<delimiter(")content(k)delimiter(")>operator(:)integer(1)operator(,) string<delimiter(")content(w)delimiter(")>operator(:)integer(2)operator(,) string<delimiter(")content(r)delimiter(")>operator(:)integer(2)operator(,) string<delimiter(")content(c)delimiter(")>operator(:)integer(1)operator(,) string<delimiter(")content(l)delimiter(")>operator(:)integer(5)operator(,) string<delimiter(")content(g)delimiter(")>operator(:)integer(1)operator(,) string<delimiter(")content(b)delimiter(")>operator(:)integer(1)operator(,) string<delimiter(")content(a)delimiter(")>operator(:)integer(2)operator(,) string<delimiter(")content(y)delimiter(")>operator(:)integer(2)operator(,) string<delimiter(")content(n)delimiter(")>operator(:)integer(1)operator(,) string<delimiter(")content(e)delimiter(")>operator(:)integer(4)(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.15)
+comment(//----------------------------------------------------------------------------------)
+ident(father) operator(=) operator([)
+ key(Cain)operator(:)string<delimiter(')content(Adam)delimiter(')>operator(,)
+ key(Abel)operator(:)string<delimiter(')content(Adam)delimiter(')>operator(,)
+ key(Seth)operator(:)string<delimiter(')content(Adam)delimiter(')>operator(,)
+ key(Enoch)operator(:)string<delimiter(')content(Cain)delimiter(')>operator(,)
+ key(Irad)operator(:)string<delimiter(')content(Enoch)delimiter(')>operator(,)
+ key(Mehujael)operator(:)string<delimiter(')content(Irad)delimiter(')>operator(,)
+ key(Methusael)operator(:)string<delimiter(')content(Mehujael)delimiter(')>operator(,)
+ key(Lamech)operator(:)string<delimiter(')content(Methusael)delimiter(')>operator(,)
+ key(Jabal)operator(:)string<delimiter(')content(Lamech)delimiter(')>operator(,)
+ key(Jubal)operator(:)string<delimiter(')content(Lamech)delimiter(')>operator(,)
+ key(Tubalcain)operator(:)string<delimiter(')content(Lamech)delimiter(')>operator(,)
+ key(Enos)operator(:)string<delimiter(')content(Seth)delimiter(')>
+(])
+
+keyword(def) method(upline)operator(()ident(person)(\)) operator({)
+ keyword(while) operator(()ident(father)operator(.)ident(containsKey)operator(()ident(person)(\)\)) operator({)
+ ident(print) ident(person) operator(+) string<delimiter(')content( )delimiter(')>
+ ident(person) operator(=) ident(father)operator([)ident(person)(])
+ (})
+ ident(println) ident(person)
+(})
+
+ident(upline)operator(()string<delimiter(')content(Irad)delimiter(')>(\))
+comment(// => Irad Enoch Cain Adam)
+
+ident(children) operator(=) operator([)operator(:)(])
+ident(father)operator(.)ident(each) operator({) ident(k)operator(,)ident(v) operator(->)
+ keyword(if) operator(()operator(!)ident(children)operator(.)ident(containsKey)operator(()ident(v)(\)\)) ident(children)operator([)ident(v)(]) operator(=) type([])
+ ident(children)operator([)ident(v)(]) operator(+=) ident(k)
+(})
+keyword(def) method(downline)operator(()ident(person)(\)) operator({)
+ ident(println) string<delimiter(")inline<inline_delimiter($)ident(person)>content( begat )inline<inline_delimiter(${)ident(children.containsKey(person\)?children[person].join(', '\):'Nobody')inline_delimiter(})>content(.)char(\\n)delimiter(")>
+(})
+ident(downline)operator(()string<delimiter(')content(Tubalcain)delimiter(')>(\))
+comment(// => Tubalcain begat Nobody.)
+ident(downline)operator(()string<delimiter(')content(Adam)delimiter(')>(\))
+comment(// => Adam begat Abel, Seth, Cain.)
+
+comment(// This one doesn't recurse through subdirectories (as a simplification\))
+comment(// scriptToFindIncludeFilesWhichContainNoIncludesScript:)
+ident(dir) operator(=) string<delimiter(')content(<path_to_usr/include>)delimiter(')>
+ident(includes) operator(=) operator([)operator(:)(])
+keyword(new) pre_type(File)operator(()ident(dir)(\))operator(.)ident(eachFile)operator({) ident(file) operator(->)
+ keyword(if) operator(()ident(file)operator(.)ident(directory)(\)) keyword(return)
+ ident(file)operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ ident(matcher) operator(=) operator(()ident(line) operator(=~) string<delimiter(')content(^)char(\\\\)content(s*#)char(\\\\)content(s*include)char(\\\\)content(s*<([^>]+\)>)delimiter(')>(\))
+ keyword(if) operator(()ident(matcher)operator(.)ident(matches)operator(()(\)\)) operator({)
+ keyword(if) operator(()operator(!)ident(includes)operator(.)ident(containsKey)operator(()ident(file)operator(.)ident(name)(\)\)) ident(includes)operator([)ident(file)operator(.)ident(name)(]) operator(=) type([])
+ ident(includes)operator([)ident(file)operator(.)ident(name)(]) operator(+=) ident(matcher)operator([)integer(0)(])operator([)integer(1)(])
+ (})
+ (})
+(})
+comment(// find referenced files which have no includes; assumes all files)
+comment(// were processed and none are missing)
+ident(println) ident(includes)operator(.)ident(values)operator(()(\))operator(.)ident(sort)operator(()(\))operator(.)ident(flatten)operator(()(\))operator(.)ident(unique)operator(()(\)) operator(-) ident(includes)operator(.)ident(keySet)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_5.16)
+comment(//----------------------------------------------------------------------------------)
+comment(// dutree - print sorted indented rendition of du output)
+comment(// obtaining this input is not shown, it is similar to other examples)
+comment(// on some unix systems it will be: duProcessFakedInput = "du options".process(\).text)
+ident(duProcessFakedInput) operator(=) string<delimiter(''')content(
+11732 groovysoap/lib
+68 groovysoap/src/main/groovy/net/soap
+71 groovysoap/src/main/groovy/net
+74 groovysoap/src/main/groovy
+77 groovysoap/src/main
+9 groovysoap/src/examples
+8 groovysoap/src/examples/groovy
+102 groovysoap/src/test
+202 groovysoap/src
+11966 groovysoap
+)delimiter(''')>
+
+comment(// The DuNode class collects all information about a directory,)
+type(class) class(DuNode) operator({)
+ keyword(def) ident(name)
+ keyword(def) ident(size)
+ keyword(def) ident(kids) operator(=) type([])
+
+ comment(// support for sorting nodes with side)
+ keyword(def) method(compareTo)operator(()ident(node2)(\)) operator({) ident(size) operator(<=)operator(>) ident(node2)operator(.)ident(size) (})
+
+ keyword(def) method(getBasename)operator(()(\)) operator({)
+ ident(name)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(.*)content(\\/)delimiter(/)>operator(,) string<delimiter(')delimiter(')>(\))
+ (})
+
+ comment(// returns substring before last "/", otherwise null)
+ keyword(def) method(getParent)operator(()(\)) operator({)
+ keyword(def) ident(p) operator(=) ident(name)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(\\/)content([^)content(\\/)content(]+)content($)delimiter(/)>operator(,)string<delimiter(')delimiter(')>(\))
+ keyword(return) operator(()ident(p) operator(==) ident(name)(\)) operator(?) keyword(null) operator(:) ident(p)
+ (})
+(})
+
+comment(// The DuTree does the actual work of)
+comment(// getting the input, parsing it, building up a tree)
+comment(// and formatting it for output)
+type(class) class(DuTree) operator({)
+ keyword(def) ident(input)
+ keyword(def) ident(topdir)
+ keyword(def) ident(nodes) operator(=) operator([)operator(:)(])
+ keyword(def) ident(dirsizes) operator(=) operator([)operator(:)(])
+ keyword(def) ident(kids) operator(=) operator([)operator(:)(])
+
+ comment(// get a node by name, create it if it does not exist yet)
+ keyword(def) method(getOrCreateNode)operator(()ident(name)(\)) operator({)
+ keyword(if) operator(()operator(!)ident(nodes)operator(.)ident(containsKey)operator(()ident(name)(\)\))
+ ident(nodes)operator([)ident(name)(]) operator(=) keyword(new) ident(DuNode)operator(()key(name)operator(:)ident(name)(\))
+ keyword(return) ident(nodes)operator([)ident(name)(])
+ (})
+
+ comment(// figure out how much is taken in each directory)
+ comment(// that isn't stored in the subdirectories. Add a new)
+ comment(// fake kid called "." containing that much.)
+ keyword(def) method(getDots)operator(()ident(node)(\)) operator({)
+ keyword(def) ident(cursize) operator(=) ident(node)operator(.)ident(size)
+ keyword(for) operator(()ident(kid) keyword(in) ident(node)operator(.)ident(kids)(\)) operator({)
+ ident(cursize) operator(-=) ident(kid)operator(.)ident(size)
+ ident(getDots)operator(()ident(kid)(\))
+ (})
+ keyword(if) operator(()ident(node)operator(.)ident(size) operator(!=) ident(cursize)(\)) operator({)
+ keyword(def) ident(newnode) operator(=) ident(getOrCreateNode)operator(()ident(node)operator(.)ident(name) operator(+) string<delimiter(")content(/.)delimiter(")>(\))
+ ident(newnode)operator(.)ident(size) operator(=) ident(cursize)
+ ident(node)operator(.)ident(kids) operator(+=) ident(newnode)
+ (})
+ (})
+
+ keyword(def) method(processInput)operator(()(\)) operator({)
+ keyword(def) ident(name) operator(=) string<delimiter(')delimiter(')>
+ ident(input)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(findAll)operator({)local_variable(it)operator(.)ident(trim)operator(()(\)})operator(.)ident(each)operator({) ident(line) operator(->)
+ keyword(def) ident(tokens) operator(=) ident(line)operator(.)ident(tokenize)operator(()(\))
+ keyword(def) ident(size) operator(=) ident(tokens)operator([)integer(0)(])
+ ident(name) operator(=) ident(tokens)operator([)integer(1)(])
+ keyword(def) ident(node) operator(=) ident(getOrCreateNode)operator(()ident(name)(\))
+ ident(node)operator(.)ident(size) operator(=) ident(size)operator(.)ident(toInteger)operator(()(\))
+ ident(nodes)operator([)ident(name)(]) operator(=) ident(node)
+ keyword(def) ident(parent) operator(=) ident(node)operator(.)ident(parent)
+ keyword(if) operator(()ident(parent)(\))
+ ident(getOrCreateNode)operator(()ident(parent)(\))operator(.)ident(kids) operator(<)operator(<) ident(node)
+ (})
+ ident(topdir) operator(=) ident(nodes)operator([)ident(name)(])
+ (})
+
+ comment(// recursively output everything)
+ comment(// passing padding and number width as well)
+ comment(// on recursive calls)
+ keyword(def) method(output)operator(()ident(node)operator(,) ident(prefix)operator(=)string<delimiter(')delimiter(')>operator(,) ident(width)operator(=)integer(0)(\)) operator({)
+ keyword(def) ident(line) operator(=) ident(node)operator(.)ident(size)operator(.)ident(toString)operator(()(\))operator(.)ident(padRight)operator(()ident(width)(\)) operator(+) string<delimiter(')content( )delimiter(')> operator(+) ident(node)operator(.)ident(basename)
+ ident(println) operator(()ident(prefix) operator(+) ident(line)(\))
+ ident(prefix) operator(+=) ident(line)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)char(\\d)content( )delimiter(/)>operator(,) string<delimiter(')content(| )delimiter(')>(\))
+ ident(prefix) operator(=) ident(prefix)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content([^|])delimiter(/)>operator(,) string<delimiter(')content( )delimiter(')>(\))
+ keyword(if) operator(()ident(node)operator(.)ident(kids)operator(.)ident(size)operator(()(\)) operator(>) integer(0)(\)) operator({) comment(// not a bachelor node)
+ ident(kids) operator(=) ident(node)operator(.)ident(kids)
+ ident(kids)operator(.)ident(sort)operator({) ident(a)operator(,)ident(b) operator(->) ident(b)operator(.)ident(compareTo)operator(()ident(a)(\)) (})
+ ident(width) operator(=) ident(kids)operator([)integer(0)(])operator(.)ident(size)operator(.)ident(toString)operator(()(\))operator(.)ident(size)operator(()(\))
+ keyword(for) operator(()ident(kid) keyword(in) ident(kids)(\)) ident(output)operator(()ident(kid)operator(,) ident(prefix)operator(,) ident(width)(\))
+ (})
+ (})
+(})
+
+ident(tree) operator(=) keyword(new) ident(DuTree)operator(()key(input)operator(:)ident(duProcessFakedInput)(\))
+ident(tree)operator(.)ident(processInput)operator(()(\))
+ident(tree)operator(.)ident(getDots)operator(()ident(tree)operator(.)ident(topdir)(\))
+ident(tree)operator(.)ident(output)operator(()ident(tree)operator(.)ident(topdir)(\))
+comment(// =>)
+comment(// 11966 groovysoap)
+comment(// | 11732 lib)
+comment(// | 202 src)
+comment(// | | 102 test)
+comment(// | | 77 main)
+comment(// | | | 74 groovy)
+comment(// | | | | 71 net)
+comment(// | | | | | 68 soap)
+comment(// | | | | | 3 .)
+comment(// | | | | 3 .)
+comment(// | | | 3 .)
+comment(// | | 14 .)
+comment(// | | 9 examples)
+comment(// | | | 8 groovy)
+comment(// | | | 1 .)
+comment(// | 32 .)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_6.0)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy has built-in language support for Regular Expressions:)
+comment(// * Strings quoted with '/' characters have special escaping)
+comment(// rules for backslashes and the like.)
+comment(// * ~string (regex pattern operator\))
+comment(// * m =~ /pattern/ (regex find operator\))
+comment(// * m ==~/pattern/ (regex match operator\))
+comment(// * patterns can be used in case expressions in a switch statement)
+comment(// * string.replaceAll can take a closure expression as the second argument)
+comment(// In addition, Groovy can make use of Java's Pattern, Matcher and Scanner classes)
+comment(// directly. (The sugar coating metnioed above sits on top of these anyway\).)
+comment(// There are also additional open source Java regex libraries which can be used.)
+
+ident(meadow1) operator(=) string<delimiter(')content(cow grass butterflies Ovine)delimiter(')>
+ident(meadow2) operator(=) string<delimiter(')content(goat sheep flowers dog)delimiter(')>
+comment(// pattern strings can benefit from 'slashy' quotes)
+ident(partial) operator(=) regexp<delimiter(/)content(sheep)delimiter(/)>
+ident(full) operator(=) regexp<delimiter(/)content(.*sheep.*)delimiter(/)>
+
+comment(// find operator)
+keyword(assert) operator(!)operator(()ident(meadow1) operator(=~) ident(partial)(\))
+keyword(assert) ident(meadow2) operator(=~) ident(partial)
+ident(finder) operator(=) operator(()ident(meadow2) operator(=~) ident(partial)(\))
+comment(// underneath Groovy sugar coating is Java implementation)
+keyword(assert) ident(finder) keyword(instanceof) ident(java)operator(.)ident(util)operator(.)ident(regex)operator(.)ident(Matcher)
+
+comment(// match operator)
+keyword(assert) operator(!)operator(()ident(meadow1) operator(==~) ident(full)(\))
+keyword(assert) ident(meadow2) operator(==~) ident(full)
+ident(matcher) operator(=) operator(()ident(meadow2) operator(==~) ident(full)(\))
+comment(// under the covers is just a boolean)
+keyword(assert) ident(matcher) keyword(instanceof) pre_type(Boolean)
+
+keyword(assert) ident(meadow1) operator(=~) regexp<delimiter(/)content((?i\))char(\\b)content(ovines?)char(\\b)delimiter(/)> comment(// (?i\) == case flag)
+
+ident(string) operator(=) string<delimiter(')content(good food)delimiter(')>
+ident(println) ident(string)operator(.)ident(replaceFirst)operator(()regexp<delimiter(/)content(o*)delimiter(/)>operator(,) string<delimiter(')content(e)delimiter(')>(\))
+comment(// => egood food)
+ident(println) ident(string)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(o*)delimiter(/)>operator(,) string<delimiter(')content(e)delimiter(')>(\))
+comment(// => egeede efeede (global\))
+comment(// beware this one is just textual replacement)
+ident(println) ident(string)operator(.)ident(replace)operator(()regexp<delimiter(/)content(o*)delimiter(/)>operator(,) string<delimiter(')content(e)delimiter(')>(\))
+comment(// => good food)
+ident(println) string<delimiter(')content(o*o*)delimiter(')>operator(.)ident(replace)operator(()regexp<delimiter(/)content(o*)delimiter(/)>operator(,) string<delimiter(')content(e)delimiter(')>(\))
+comment(// => ee)
+
+comment(// groovy -e "m = args[0] =~ /(a|ba|b\)+(a|ac\)+/; if (m.matches(\)\) println m[0][0]" ababacaca)
+comment(// => ababa)
+
+ident(digits) operator(=) string<delimiter(")content(123456789)delimiter(")>
+ident(nonlap) operator(=) ident(digits) operator(=~) regexp<delimiter(/)char(\\d)char(\\d)char(\\d)delimiter(/)>
+keyword(assert) ident(nonlap)operator(.)ident(count) operator(==) integer(3)
+ident(print) string<delimiter(')content(Non-overlapping: )delimiter(')>
+operator(()integer(0)operator(..<)ident(nonlap)operator(.)ident(count)(\))operator(.)ident(each)operator({) ident(print) ident(nonlap)operator([)local_variable(it)(]) operator(+) string<delimiter(')content( )delimiter(')> (})operator(;) ident(print) string<delimiter(')content(\\n)delimiter(')>
+ident(print) string<delimiter(')content(Overlapping: )delimiter(')>
+ident(yeslap) operator(=) operator(()ident(digits) operator(=~) regexp<delimiter(/)content((?=()char(\\d)char(\\d)char(\\d)content(\)\))delimiter(/)>(\))
+keyword(assert) ident(yeslap)operator(.)ident(count) operator(==) integer(7)
+operator(()integer(0)operator(..<)ident(yeslap)operator(.)ident(count)(\))operator(.)ident(each)operator({) ident(print) ident(yeslap)operator([)local_variable(it)(])operator([)integer(1)(]) operator(+) string<delimiter(')content( )delimiter(')> (})operator(;) ident(print) string<delimiter(')content(\\n)delimiter(')>
+comment(// Non-overlapping: 123 456 789)
+comment(// Overlapping: 123 234 345 456 567 678 789)
+
+ident(string) operator(=) string<delimiter(')content(And little lambs eat ivy)delimiter(')>
+comment(// Greedy version)
+ident(parts) operator(=) ident(string) operator(=~) regexp<delimiter(/)content((.*\)(l[^s]*s\)(.*\))delimiter(/)>
+operator(()integer(1)operator(..)ident(parts)operator(.)ident(groupCount)operator(()(\)\))operator(.)ident(each)operator({) ident(print) string<delimiter(")content(()inline<inline_delimiter(${)ident(parts[0][it])inline_delimiter(})>content(\) )delimiter(")> (})operator(;) ident(print) string<delimiter(')content(\\n)delimiter(')>
+comment(// (And little \) (lambs\) ( eat ivy\))
+
+comment(// Reluctant version)
+ident(parts) operator(=) ident(string) operator(=~) regexp<delimiter(/)content((.*?\)(l[^s]*s\)(.*\))delimiter(/)>
+operator(()integer(1)operator(..)ident(parts)operator(.)ident(groupCount)operator(()(\)\))operator(.)ident(each)operator({) ident(print) string<delimiter(")content(()inline<inline_delimiter(${)ident(parts[0][it])inline_delimiter(})>content(\) )delimiter(")> (})operator(;) ident(print) string<delimiter(')content(\\n)delimiter(')>
+comment(// (And \) (little lambs\) ( eat ivy\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.1)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy splits src and dest to avoid this problem)
+ident(src) operator(=) string<delimiter(')content(Go this way)delimiter(')>
+ident(dst) operator(=) ident(src)operator(.)ident(replaceFirst)operator(()string<delimiter(')content(this)delimiter(')>operator(,) string<delimiter(')content(that)delimiter(')>(\))
+keyword(assert) ident(dst) operator(==) string<delimiter(')content(Go that way)delimiter(')>
+
+comment(// extract basename)
+ident(src) operator(=) string<delimiter(')content(c:/some/path/file.ext)delimiter(')>
+ident(dst) operator(=) ident(src)operator(.)ident(replaceFirst)operator(()string<delimiter(')content(^.*/)delimiter(')>operator(,) string<delimiter(')delimiter(')>(\))
+keyword(assert) ident(dst) operator(==) string<delimiter(')content(file.ext)delimiter(')>
+
+comment(// Make All Words Title-Cased (not that you would do it this way\))
+comment(// The preprocessing operations \\X where X is one of l, u, L, and U are not supported)
+comment(// in the sun regex library but other Java regex libraries may support this. Instead:)
+ident(src) operator(=) string<delimiter(')content(make all words title-cased)delimiter(')>
+ident(dst) operator(=) ident(src)
+operator(()string<delimiter(')content(a)delimiter(')>operator(..)string<delimiter(')content(z)delimiter(')>(\))operator(.)ident(each)operator({) ident(dst) operator(=) ident(dst)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(([^a-zA-Z]\))delimiter(/)>operator(+)local_variable(it)operator(+)regexp<delimiter(/)content(|)content(\\A)delimiter(/)>operator(+)local_variable(it)operator(,) regexp<delimiter(/)content($)content(1)delimiter(/)>operator(+)local_variable(it)operator(.)ident(toUpperCase)operator(()(\)\)) (})
+keyword(assert) ident(dst) operator(==) string<delimiter(')content(Make All Words Title-Cased)delimiter(')>
+
+comment(// rename list of dirs)
+ident(bindirs) operator(=) string<delimiter(')content(/usr/bin /bin /usr/local/bin)delimiter(')>operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))operator(.)ident(toList)operator(()(\))
+ident(expected) operator(=) string<delimiter(')content(/usr/lib /lib /usr/local/lib)delimiter(')>operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))operator(.)ident(toList)operator(()(\))
+ident(libdirs) operator(=) ident(bindirs)operator(.)ident(collect) operator({) ident(dir) operator(->) ident(dir)operator(.)ident(replaceFirst)operator(()string<delimiter(')content(bin)delimiter(')>operator(,) string<delimiter(')content(lib)delimiter(')>(\)) (})
+keyword(assert) ident(libdirs) operator(==) ident(expected)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy uses Java regex (other Java regex packages would also be possible\))
+comment(// It doesn't support Locale-based settings but you can roll your own to some)
+comment(// extent, you can use any Unicode characters as per below and you can use)
+comment(// \\p{Punct} Punctuation: One of !"#$%&'(\)*+,-./:;<=>?@[\\]^_`{|}~)
+comment(// or the other special character classes)
+ident(words) operator(=) string<delimiter(''')content(
+silly
+façade
+coöperate
+niño
+Renée
+Moliçre
+hæmoglobin
+naïve
+tschüß
+random!stuff#here)content(\\u)content(0948
+)delimiter(''')>
+ident(results) operator(=) string<delimiter(')delimiter(')>
+ident(greekAlpha) operator(=) string<delimiter(')content(\\u0391)delimiter(')>
+ident(special) operator(=) string<delimiter(')content(çéüßöñàæï?)delimiter(')> operator(+) ident(greekAlpha)
+comment(// flag as either Y (alphabetic\) or N (not\))
+ident(words)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(findAll)operator({)local_variable(it)operator(.)ident(trim)operator(()(\)})operator(.)ident(each)operator({) ident(results) operator(+=) local_variable(it) operator(==~) regexp<delimiter(/)content(^[)char(\\w)delimiter(/)>operator(+)ident(special)operator(+)regexp<delimiter(/)content(]+)content($)delimiter(/)> operator(?)string<delimiter(')content(Y)delimiter(')>operator(:)string<delimiter(')content(N)delimiter(')> (})
+keyword(assert) ident(results) operator(==) string<delimiter(')content(YYYYYYYYYN)delimiter(')>
+ident(results) operator(=) string<delimiter(')delimiter(')>
+ident(words)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(findAll)operator({)local_variable(it)operator(.)ident(trim)operator(()(\)})operator(.)ident(each)operator({) ident(results) operator(+=) local_variable(it) operator(==~) regexp<delimiter(/)content(^[^)content(\\p)content({Punct}]+)content($)delimiter(/)> operator(?)string<delimiter(')content(Y)delimiter(')>operator(:)string<delimiter(')content(N)delimiter(')> (})
+keyword(assert) ident(results) operator(==) string<delimiter(')content(YYYYYYYYYN)delimiter(')>
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// as many non-whitespace bytes as possible)
+ident(finder) operator(=) string<delimiter(')content(abczqz z)delimiter(')> operator(=~) regexp<delimiter(/)content(a)char(\\S)content(+z)delimiter(/)>
+keyword(assert) ident(finder)operator([)integer(0)(]) operator(==) string<delimiter(')content(abczqz)delimiter(')>
+
+comment(// as many letters, apostrophes, and hyphens)
+ident(finder) operator(=) string<delimiter(")content(aAzZ'z-z0z)delimiter(")> operator(=~) regexp<delimiter(/)content(a[A-Za-z'-]+z)delimiter(/)> comment(//')
+keyword(assert) ident(finder)operator([)integer(0)(]) operator(==) string<delimiter(")content(aAzZ'z-z)delimiter(")>
+
+comment(// selecting words)
+ident(finder) operator(=) string<delimiter(')content(23rd Psalm)delimiter(')> operator(=~) regexp<delimiter(/)char(\\b)content(([A-Za-z]+\))char(\\b)delimiter(/)> comment(// usually best)
+ident(println) ident(finder)operator([)integer(0)(])operator([)integer(0)(])
+comment(// => Psalm (23rd is not matched\))
+ident(finder) operator(=) string<delimiter(')content(23rd Psalm)delimiter(')> operator(=~) regexp<delimiter(/)char(\\s)content(([A-Za-z]+\))char(\\s)delimiter(/)> comment(// fails at ends or w/ punctuation)
+ident(println) ident(finder)operator(.)ident(matches)operator(()(\))
+comment(// => false (no whitespaces at ends\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.4)
+comment(//----------------------------------------------------------------------------------)
+ident(str) operator(=) string<delimiter(')content(groovy.codehaus.org and www.aboutgroovy.com)delimiter(')>
+ident(re) operator(=) string<delimiter(''')content((?x\) # to enable whitespace and comments
+ ( # capture the hostname in )content($)content(1
+ (?: # these parens for grouping only
+ (?! [-_] \) # lookahead for neither underscore nor dash
+ [)content(\\\\)content(w-] + # hostname component
+ )content(\\\\)content(. # and the domain dot
+ \) + # now repeat that whole thing a bunch of times
+ [A-Za-z] # next must be a letter
+ [)content(\\\\)content(w-] + # now trailing domain part
+ \) # end of )content($)content(1 capture
+ )delimiter(''')>
+
+ident(finder) operator(=) ident(str) operator(=~) ident(re)
+ident(out) operator(=) ident(str)
+operator(()integer(0)operator(..<)ident(finder)operator(.)ident(count)(\))operator(.)ident(each)operator({)
+ ident(adr) operator(=) ident(finder)operator([)local_variable(it)(])operator([)integer(0)(])
+ ident(out) operator(=) ident(out)operator(.)ident(replaceAll)operator(()ident(adr)operator(,) string<delimiter(")inline<inline_delimiter($)ident(adr)>content( [)inline<inline_delimiter(${)ident(InetAddress.getByName(adr\).hostAddress)inline_delimiter(})>content(])delimiter(")>(\))
+(})
+ident(println) ident(out)
+comment(// => groovy.codehaus.org [63.246.7.187] and www.aboutgroovy.com [63.246.7.76])
+
+comment(// to match whitespace or #-characters in an extended re you need to escape them.)
+ident(foo) operator(=) integer(42)
+ident(str) operator(=) string<delimiter(')content(blah #foo# blah)delimiter(')>
+ident(re) operator(=) string<delimiter(''')content((?x\) # to enable whitespace and comments
+ )content(\\\\)content(# # a pound sign
+ ()content(\\\\)content(w+\) # the variable name
+ )content(\\\\)content(# # another pound sign
+ )delimiter(''')>
+ident(finder) operator(=) ident(str) operator(=~) ident(re)
+ident(found) operator(=) ident(finder)operator([)integer(0)(])
+ident(out) operator(=) ident(str)operator(.)ident(replaceAll)operator(()ident(found)operator([)integer(0)(])operator(,) ident(evaluate)operator(()ident(found)operator([)integer(1)(]\))operator(.)ident(toString)operator(()(\)\))
+keyword(assert) ident(out) operator(==) string<delimiter(')content(blah 42 blah)delimiter(')>
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.5)
+comment(//----------------------------------------------------------------------------------)
+ident(fish) operator(=) string<delimiter(')content(One fish two fish red fish blue fish)delimiter(')>
+ident(expected) operator(=) string<delimiter(')content(The third fish is a red one.)delimiter(')>
+ident(thirdFish) operator(=) regexp<delimiter(/)content((?:)char(\\w)content(+)char(\\s)content(+fish)char(\\s)content(+\){2}()char(\\w)content(+\))char(\\s)content(+fish.*)delimiter(/)>
+keyword(assert) ident(expected) operator(==) operator(()ident(fish)operator(.)ident(replaceAll)operator(()ident(thirdFish)operator(,) string<delimiter(')content(The third fish is a )content($)content(1 one.)delimiter(')>(\)\))
+
+ident(anyFish) operator(=) regexp<delimiter(/)content(()char(\\w)content(+\))char(\\s)content(+fish)char(\\b)delimiter(/)>
+ident(finder) operator(=) ident(fish) operator(=~) ident(anyFish)
+comment(// finder contains an array of matched groups)
+comment(// 2 = third one (index start at 0\), 1 = matched word in group)
+ident(out) operator(=) string<delimiter(")content(The third fish is a )inline<inline_delimiter(${)ident(finder[2][1])inline_delimiter(})>content( one.)delimiter(")>
+keyword(assert) ident(out) operator(==) ident(expected)
+
+ident(evens) operator(=) type([])
+operator(()integer(0)operator(..<)ident(finder)operator(.)ident(count)(\))operator(.)ident(findAll)operator({)local_variable(it)operator(%)integer(2)operator(!=)integer(0)(})operator(.)ident(each)operator({) ident(evens) operator(+=) ident(finder)operator([)local_variable(it)(])operator([)integer(1)(]) (})
+ident(println) string<delimiter(")content(Even numbered fish are )inline<inline_delimiter(${)ident(evens.join(' '\))inline_delimiter(})>content(.)delimiter(")>
+comment(// => Even numbered fish are two blue.)
+
+comment(// one of several ways to do this)
+ident(pond) operator(=) ident(fish) operator(+) string<delimiter(')content( in the pond)delimiter(')>
+ident(fishInPond) operator(=) operator(()regexp<delimiter(/)content(()char(\\w)content(+\)()char(\\s)content(+fish)char(\\b)char(\\s)content(*\))delimiter(/)>(\)) operator(*) integer(4) operator(+) regexp<delimiter(/)content((.*\))delimiter(/)>
+ident(found) operator(=) operator(()ident(pond) operator(=~) ident(fishInPond)(\))operator([)integer(0)(])
+ident(println) operator(()operator(()ident(found)operator([)integer(1)operator(..)integer(6)(]) operator(+) string<delimiter(')content(sushi)delimiter(')> operator(+) ident(found)operator([)integer(8)operator(..)integer(9)(]\))operator(.)ident(join)operator(()(\)\))
+comment(// => One fish two fish red fish sushi fish in the pond)
+
+comment(// find last fish)
+ident(expected) operator(=) string<delimiter(')content(Last fish is blue)delimiter(')>
+ident(pond) operator(=) string<delimiter(')content(One fish two fish red fish blue fish swim here.)delimiter(')>
+ident(finder) operator(=) operator(()ident(pond) operator(=~) ident(anyFish)(\))
+keyword(assert) ident(expected) operator(==) string<delimiter(")content(Last fish is )inline<inline_delimiter(${)ident(finder[finder.count-1][1])inline_delimiter(})>delimiter(")>
+comment(// => Last fish is blue)
+
+comment(// greedy match version of above)
+ident(finder) operator(=) operator(()ident(pond) operator(=~) regexp<delimiter(/)content(.*)char(\\b)delimiter(/)> operator(+) ident(anyFish)(\))
+keyword(assert) ident(expected) operator(==) string<delimiter(")content(Last fish is )inline<inline_delimiter(${)ident(finder[0][1])inline_delimiter(})>delimiter(")>
+
+comment(// last fish match version of above)
+ident(finder) operator(=) operator(()ident(pond) operator(=~) regexp<delimiter(/)char(\\b)content(()char(\\w)content(+\))char(\\s)content(+fish)char(\\b)content((?!.*)char(\\b)content(fish)char(\\b)content(\))delimiter(/)>(\))
+keyword(assert) ident(expected) operator(==) string<delimiter(")content(Last fish is )inline<inline_delimiter(${)ident(finder[0][1])inline_delimiter(})>delimiter(")>
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// Html Stripper)
+comment(// get this using: fakedfile = new File('path_to_file.htm'\).text)
+ident(fakedFile) operator(=) string<delimiter(''')content(
+<html>
+<head><title>Chapter 1 Title</title></head>
+<body>
+<h1>Chapter 1: Some Heading</h1>
+A paragraph.
+</body>
+</html>
+)delimiter(''')>
+
+ident(stripExpectations) operator(=) string<delimiter(''')content(
+Chapter 1 Title
+
+Chapter 1: Some Heading
+A paragraph.
+)delimiter(''')>operator(.)ident(trim)operator(()(\))
+
+ident(stripped) operator(=) ident(fakedFile)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((?m\)<.*?>)delimiter(/)>operator(,)string<delimiter(')delimiter(')>(\))operator(.)ident(trim)operator(()(\))
+keyword(assert) ident(stripExpectations) operator(==) ident(stripped)
+
+ident(pattern) operator(=) string<delimiter(''')content((?x\)
+ ( # capture in )content($)content(1
+ Chapter # text string
+ )content(\\\\)content(s+ # mandatory whitespace
+ )content(\\\\)content(d+ # decimal number
+ )content(\\\\)content(s* # optional whitespace
+ : # a real colon
+ . * # anything not a newline till end of line
+ \)
+)delimiter(''')>
+
+ident(headerfyExpectations) operator(=) string<delimiter(''')content(
+Chapter 1 Title
+
+<H1>Chapter 1: Some Heading</H1>
+A paragraph.
+)delimiter(''')>operator(.)ident(trim)operator(()(\))
+
+ident(headerfied) operator(=) ident(stripped)operator(.)ident(replaceAll)operator(()ident(pattern)operator(,) string<delimiter(')content(<H1>)content($)content(1</H1>)delimiter(')>(\))
+keyword(assert) ident(headerfyExpectations) operator(==) ident(headerfied)
+
+comment(// one liner equivalent which prints to stdout)
+comment(//% groovy -p -e "line.replaceAll(/^(Chapter\\s+\\d+\\s*:.*\)/,'<H1>$1</H1>'\)")
+
+comment(// one liner equivalent which modifies file in place and creates *.bak original file)
+comment(//% groovy -pi .bak -e "line.replaceAll(/^(Chapter\\s+\\d+\\s*:.*\)/,'<H1>$1</H1>'\)")
+
+comment(// use: realFileInput = new File(path_to_file\).text)
+ident(fakeFileInput) operator(=) string<delimiter(''')content(
+0
+START
+1
+2
+END
+3
+4
+5
+START
+6
+END
+)delimiter(''')>
+
+ident(chunkyPattern) operator(=) regexp<delimiter(/)content((?ms\)^START(.*?\)^END)delimiter(/)>
+ident(finder) operator(=) ident(fakeFileInput) operator(=~) ident(chunkyPattern)
+operator(()integer(0)operator(..<)ident(finder)operator(.)ident(count)(\))operator(.)ident(each) operator({)
+ ident(println) string<delimiter(")content(Chunk #)inline<inline_delimiter($)local_variable(it)>content( contains )inline<inline_delimiter(${)ident(new StringTokenizer(finder[it][1],'\\n'\).countTokens(\))inline_delimiter(})>content( lines.)delimiter(")>
+(})
+comment(// =>)
+comment(// Chunk #0 contains 2 lines.)
+comment(// Chunk #1 contains 1 lines.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.7)
+comment(//----------------------------------------------------------------------------------)
+comment(// general pattern is:)
+comment(//file = new File("datafile"\).text.split(/pattern/\))
+comment(// .Ch, .Se and .Ss divide chunks of input text)
+ident(fakedFiletext) operator(=) string<delimiter(''')content(
+.Ch
+abc
+.Se
+def
+.Ss
+ghi
+.Se
+jkl
+.Se
+mno
+.Ss
+pqr
+.Ch
+stu
+.Ch
+vwx
+.Se
+yz!
+)delimiter(''')>
+ident(chunks) operator(=) ident(fakedFiletext)operator(.)ident(split)operator(()regexp<delimiter(/)content((?m\)^)content(\\.)content((Ch|Se|Ss\))content($)delimiter(/)>(\))
+ident(println) string<delimiter(")content(I read )inline<inline_delimiter(${)ident(chunks.size(\))inline_delimiter(})>content( chunks.)delimiter(")>
+comment(// => I read 10 chunks.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.8)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy doesn't support the ~/BEGIN/ .. ~/END/ notation)
+comment(// you have to emulate it as shown in the example below)
+comment(// The from line number to line number processing is supported)
+comment(// from the command line but not within a script, e.g.)
+comment(// command-line to print lines 15 through 17 inclusive (see below\))
+comment(// > groovy -p -e "if (count in 15..17\) return line" datafile)
+comment(// Within a script itself, you emulate the count by keeping state)
+
+ident(htmlContent) operator(=) string<delimiter(''')content(
+<h1>A Heading</h1>
+Here is <XMP>inline AAA</XMP>.
+And the bigger Example 2:
+<XMP>
+line BBB
+line CCC
+</XMP>
+Done.
+)delimiter(''')>operator(.)ident(trim)operator(()(\))
+
+ident(examplePattern) operator(=) regexp<delimiter(/)content((?ms\)<XMP>(.*?\)<)content(\\/)content(XMP>)delimiter(/)>
+ident(finder) operator(=) ident(htmlContent) operator(=~) ident(examplePattern)
+operator(()integer(0)operator(..<)ident(finder)operator(.)ident(count)(\))operator(.)ident(each) operator({)
+ ident(println) string<delimiter(")content(Example )inline<inline_delimiter(${)ident(it+1)inline_delimiter(})>content(:)delimiter(")>
+ ident(println) ident(finder)operator([)local_variable(it)(])operator([)integer(1)(])
+(})
+comment(// =>)
+comment(// Example 1:)
+comment(// inline AAA)
+comment(// Example 2:)
+comment(//)
+comment(// line BBB)
+comment(// line CCC)
+comment(//)
+
+ident(htmlContent)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(eachWithIndex)operator({) ident(line)operator(,) ident(count) operator(->)
+ keyword(if) operator(()ident(count) keyword(in) integer(4)operator(..)integer(5)(\)) ident(println) ident(line)
+(})
+comment(// =>)
+comment(// line BBB)
+comment(// line CCC)
+
+comment(// You would probably use a mail Api for this in Groovy)
+ident(fakedMailInput) operator(=) string<delimiter(''')content(
+From: A Person <someone@somewhere.com>
+To: <pleac-discuss@lists.sourceforge.net>
+Date: Sun, 31 Dec 2006 02:14:57 +1100
+
+From: noone@nowhere.com
+To: <pleac-discuss@lists.sourceforge.net>
+Date: Sun, 31 Dec 2006 02:14:58 +1100
+
+From: someone@somewhere.com
+To: <pleac-discuss@lists.sourceforge.net>
+Date: Sun, 31 Dec 2006 02:14:59 +1100
+)delimiter(''')>operator(.)ident(trim)operator(()(\))operator(+)string<delimiter(')content(\\n)delimiter(')>
+
+ident(seen) operator(=) operator([)operator(:)(])
+ident(fakedMailInput)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(each)operator({) ident(line) operator(->)
+ ident(m) operator(=) operator(()ident(line) operator(=~) regexp<delimiter(/)content(^From:?)char(\\s)content((.*\))delimiter(/)>(\))
+ keyword(if) operator(()ident(m)(\)) operator({)
+ ident(addr) operator(=) ident(m)operator([)integer(0)(])operator([)integer(1)(]) operator(=~) regexp<delimiter(/)content(([^<>(\),;)char(\\s)content(]+)content(\\@)content([^<>(\),;)char(\\s)content(]+\))delimiter(/)>
+ ident(x) operator(=) ident(addr)operator([)integer(0)(])operator([)integer(1)(])
+ keyword(if) operator(()ident(seen)operator(.)ident(containsKey)operator(()ident(x)(\)\)) ident(seen)operator([)ident(x)(]) operator(+=) integer(1) keyword(else) ident(seen)operator([)ident(x)(]) operator(=) integer(1)
+ (})
+(})
+ident(seen)operator(.)ident(each)operator({) ident(k)operator(,)ident(v) operator(->) ident(println) string<delimiter(")content(Address )inline<inline_delimiter($)ident(k)>content( seen )inline<inline_delimiter($)ident(v)>content( time)inline<inline_delimiter(${)ident(v==1?'':'s')inline_delimiter(})>content(.)delimiter(")> (})
+comment(// =>)
+comment(// Address noone@nowhere.com seen 1 time.)
+comment(// Address someone@somewhere.com seen 2 times.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.9)
+comment(//----------------------------------------------------------------------------------)
+keyword(import) include(java.util.regex.Pattern)
+
+ident(names) operator(=) string<delimiter(''')content(
+myFile.txt
+oldFile.tex
+myPicture.jpg
+)delimiter(''')>
+
+keyword(def) method(glob2pat)operator(()ident(globstr)(\)) operator({)
+ keyword(def) ident(patmap) operator(=) operator([) string<delimiter(')content(*)delimiter(')>operator(:)string<delimiter(')content(.*)delimiter(')>operator(,) string<delimiter(')content(?)delimiter(')>operator(:)string<delimiter(')content(.)delimiter(')>operator(,) string<delimiter(')content([)delimiter(')>operator(:)string<delimiter(')content([)delimiter(')>operator(,) string<delimiter(')content(])delimiter(')>operator(:)string<delimiter(')content(])delimiter(')> (])
+ keyword(def) ident(result) operator(=) string<delimiter(')content((?m\)^)delimiter(')>
+ string<delimiter(')content(^)delimiter(')> operator(+) ident(globstr)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((.\))delimiter(/)>(\)) operator({) ident(all)operator(,) ident(c) operator(->)
+ ident(result) operator(+=) operator(()ident(patmap)operator(.)ident(containsKey)operator(()ident(c)(\)) operator(?) ident(patmap)operator([)ident(c)(]) operator(:) pre_type(Pattern)operator(.)ident(quote)operator(()ident(c)(\)\))
+ (})
+ ident(result) operator(+) string<delimiter(')content($)delimiter(')>
+(})
+
+keyword(def) method(checkNumMatches)operator(()ident(pat)operator(,) ident(count)(\)) operator({)
+ keyword(assert) operator(()ident(names) operator(=~) ident(glob2pat)operator(()ident(pat)(\)\))operator(.)ident(count) operator(==) ident(count)
+(})
+
+ident(checkNumMatches)operator(()string<delimiter(')content(*.*)delimiter(')>operator(,) integer(3)(\))
+ident(checkNumMatches)operator(()string<delimiter(')content(my*.*)delimiter(')>operator(,) integer(2)(\))
+ident(checkNumMatches)operator(()string<delimiter(')content(*.t*)delimiter(')>operator(,) integer(2)(\))
+ident(checkNumMatches)operator(()string<delimiter(')content(*File.*)delimiter(')>operator(,) integer(2)(\))
+ident(checkNumMatches)operator(()string<delimiter(')content(*Rabbit*.*)delimiter(')>operator(,) integer(0)(\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.10)
+comment(//----------------------------------------------------------------------------------)
+comment(// version 1: simple obvious way)
+ident(states) operator(=) string<delimiter(')content(CO ON MI WI MN)delimiter(')>operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))operator(.)ident(toList)operator(()(\))
+
+keyword(def) method(popgrep1)operator(()ident(file)(\)) operator({)
+ ident(file)operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ keyword(if) operator(()ident(states)operator(.)ident(any)operator({) ident(line) operator(=~) regexp<delimiter(/)char(\\b)inline<inline_delimiter($)local_variable(it)>char(\\b)delimiter(/)> (}\)) ident(println) ident(line)
+ (})
+(})
+comment(// popgrep1(new File('path_to_file'\)\))
+
+comment(// version 2: eval strings; fast but hard to quote (SLOW\))
+keyword(def) method(popgrep2)operator(()ident(file)(\)) operator({)
+ keyword(def) ident(code) operator(=) string<delimiter(')content(def found = false)content(\\n)delimiter(')>
+ ident(states)operator(.)ident(each)operator({)
+ ident(code) operator(+=) string<delimiter(")content(if (!found && line =~ /)char(\\\\)content(b)inline<inline_delimiter($)local_variable(it)>char(\\\\)content(b/\) found = true)char(\\n)delimiter(")>
+ (})
+ ident(code) operator(+=) string<delimiter(")content(if (found\) println line)char(\\n)delimiter(")>
+ ident(file)operator(.)ident(eachLine)operator({) ident(line) operator(=) local_variable(it)operator(;) ident(evaluate)operator(()ident(code)(\)) (})
+(})
+comment(// popgrep2(new File('path_to_file'\)\))
+
+comment(// version 2b: eval using switch/case (not in Perl cookbook\) (SLOW\))
+keyword(def) method(popgrep2b)operator(()ident(file)(\)) operator({)
+ keyword(def) ident(code) operator(=) string<delimiter(')content(switch(line\) {)content(\\n)delimiter(')>
+ ident(states)operator(.)ident(each)operator({)
+ ident(code) operator(+=) string<delimiter(")content(case ~/.*)char(\\\\)content(b)inline<inline_delimiter($)local_variable(it)>char(\\\\)content(b.*/:)char(\\n)content(println line;break)char(\\n)delimiter(")>
+ (})
+ ident(code) operator(+=) string<delimiter(")content(default:break)char(\\n)content(})char(\\n)delimiter(")>
+ ident(file)operator(.)ident(eachLine)operator({) ident(line) operator(=) local_variable(it)operator(;) ident(evaluate)operator(()ident(code)(\)) (})
+(})
+comment(// popgrep2b(new File('path_to_file'\)\))
+
+comment(// version3: build a match_any function as a GString)
+keyword(def) method(popgrep3)operator(()ident(file)(\)) operator({)
+ keyword(def) ident(code) operator(=) ident(states)operator(.)ident(collect)operator({) string<delimiter(")content(line =~ /)char(\\\\)content(b)inline<inline_delimiter($)local_variable(it)>char(\\\\)content(b/)delimiter(")> (})operator(.)ident(join)operator(()string<delimiter(')content(||)delimiter(')>(\))
+ ident(file)operator(.)ident(eachLine)operator({) ident(line) operator(=) local_variable(it)operator(;) keyword(if) operator(()ident(evaluate)operator(()ident(code)(\)\)) ident(println) ident(line) (})
+(})
+comment(// popgrep3(new File('path_to_file'\)\))
+
+comment(// version4: pretty fast, but simple: compile all re's first:)
+ident(patterns) operator(=) ident(states)operator(.)ident(collect)operator({) operator(~)regexp<delimiter(/)char(\\b)inline<inline_delimiter($)local_variable(it)>char(\\b)delimiter(/)> (})
+keyword(def) method(popgrep4)operator(()ident(file)(\)) operator({)
+ ident(file)operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ keyword(if) operator(()ident(patterns)operator(.)ident(any)operator({) local_variable(it)operator(.)ident(matcher)operator(()ident(line)(\)}\)) ident(println) ident(line)
+ (})
+(})
+comment(// popgrep4(new File('path_to_file'\)\))
+
+comment(// version5: faster)
+ident(str) operator(=) ident(states)operator(.)ident(collect)operator({) regexp<delimiter(/)char(\\b)inline<inline_delimiter($)local_variable(it)>char(\\b)delimiter(/)> (})operator(.)ident(join)operator(()string<delimiter(')content(|)delimiter(')>(\))
+keyword(def) method(popgrep5)operator(()ident(file)(\)) operator({)
+ ident(file)operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ keyword(if) operator(()ident(line) operator(=~) ident(str)(\)) ident(println) ident(line)
+ (})
+(})
+comment(// popgrep5(new File('path_to_file'\)\))
+
+comment(// version5b: faster (like 5 but compiled outside loop\))
+ident(pattern) operator(=) operator(~)ident(states)operator(.)ident(collect)operator({) regexp<delimiter(/)char(\\b)inline<inline_delimiter($)local_variable(it)>char(\\b)delimiter(/)> (})operator(.)ident(join)operator(()string<delimiter(')content(|)delimiter(')>(\))
+keyword(def) method(popgrep5b)operator(()ident(file)(\)) operator({)
+ ident(file)operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ keyword(if) operator(()ident(pattern)operator(.)ident(matcher)operator(()ident(line)(\)\)) ident(println) ident(line)
+ (})
+(})
+comment(// popgrep5b(new File('path_to_file'\)\))
+
+comment(// speeds trials ON the current source file (~1200 lines\))
+comment(// popgrep1 => 0.39s)
+comment(// popgrep2 => 25.08s)
+comment(// popgrep2b => 23.86s)
+comment(// popgrep3 => 22.42s)
+comment(// popgrep4 => 0.12s)
+comment(// popgrep5 => 0.05s)
+comment(// popgrep5b => 0.05s)
+comment(// Groovy's built-in support is the way to go in terms of)
+comment(// both speed and simplicity of understanding. Avoid using)
+comment(// evaluate(\) unless you absolutely need it)
+
+comment(// generic matching functions)
+ident(input) operator(=) string<delimiter(''')content(
+both cat and dog
+neither
+just a cat
+just a dog
+)delimiter(''')>operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(findAll)operator({)local_variable(it)operator(.)ident(trim)operator(()(\)})
+
+keyword(def) method(matchAny)operator(()ident(line)operator(,) ident(patterns)(\)) operator({) ident(patterns)operator(.)ident(any)operator({) ident(line) operator(=~) local_variable(it) (}) (})
+keyword(def) method(matchAll)operator(()ident(line)operator(,) ident(patterns)(\)) operator({) ident(patterns)operator(.)ident(every)operator({) ident(line) operator(=~) local_variable(it) (}) (})
+
+keyword(assert) ident(input)operator(.)ident(findAll)operator({) ident(matchAny)operator(()local_variable(it)operator(,) operator([)string<delimiter(')content(cat)delimiter(')>operator(,)string<delimiter(')content(dog)delimiter(')>(]\)) (})operator(.)ident(size)operator(()(\)) operator(==) integer(3)
+keyword(assert) ident(input)operator(.)ident(findAll)operator({) ident(matchAny)operator(()local_variable(it)operator(,) operator([)string<delimiter(')content(cat)content($)delimiter(')>operator(,)string<delimiter(')content(^n.*)delimiter(')>(]\)) (})operator(.)ident(size)operator(()(\)) operator(==) integer(2)
+keyword(assert) ident(input)operator(.)ident(findAll)operator({) ident(matchAll)operator(()local_variable(it)operator(,) operator([)string<delimiter(')content(cat)delimiter(')>operator(,)string<delimiter(')content(dog)delimiter(')>(]\)) (})operator(.)ident(size)operator(()(\)) operator(==) integer(1)
+keyword(assert) ident(input)operator(.)ident(findAll)operator({) ident(matchAll)operator(()local_variable(it)operator(,) operator([)string<delimiter(')content(cat)content($)delimiter(')>operator(,)string<delimiter(')content(^n.*)delimiter(')>(]\)) (})operator(.)ident(size)operator(()(\)) operator(==) integer(0)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// patternCheckingScript:)
+ident(prompt) operator(=) string<delimiter(')content(\\n)content(> )delimiter(')>
+ident(print) string<delimiter(')content(Enter patterns to check:)delimiter(')> operator(+) ident(prompt)
+keyword(new) pre_type(BufferedReader)operator(()keyword(new) pre_type(InputStreamReader)operator(()pre_type(System)operator(.)ident(in)(\)\))operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ keyword(try) operator({)
+ pre_type(Pattern)operator(.)ident(compile)operator(()ident(line)(\))
+ ident(print) string<delimiter(')content(Valid)delimiter(')> operator(+) ident(prompt)
+ (}) keyword(catch) operator(()ident(java)operator(.)ident(util)operator(.)ident(regex)operator(.)ident(PatternSyntaxException) ident(ex)(\)) operator({)
+ ident(print) string<delimiter(')content(Invalid pattern: )delimiter(')> operator(+) ident(ex)operator(.)ident(message) operator(+) ident(prompt)
+ (})
+(})
+comment(// =>)
+comment(// Enter patterns to check:)
+comment(// > ab*.c)
+comment(// Valid)
+comment(// > ^\\s+[^a-z]*$)
+comment(// Valid)
+comment(// > **)
+comment(// Invalid pattern: Dangling meta character '*' near index 0)
+comment(// **)
+comment(// ^)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.12)
+comment(//----------------------------------------------------------------------------------)
+ident(src) operator(=) string<delimiter(')content(dierk könig)delimiter(')>
+comment(// simplistic with locale issue)
+ident(dst) operator(=) ident(src)
+operator(()string<delimiter(')content(a)delimiter(')>operator(..)string<delimiter(')content(z)delimiter(')>(\))operator(.)ident(each)operator({) ident(dst) operator(=) ident(dst)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((?<=[^a-zA-Z]\))delimiter(/)>operator(+)local_variable(it)operator(+)regexp<delimiter(/)content(|)content(\\A)delimiter(/)>operator(+)local_variable(it)operator(,) local_variable(it)operator(.)ident(toUpperCase)operator(()(\)\)) (})
+ident(println) ident(dst)
+comment(// => Dierk KöNig)
+comment(// locale avoidance)
+ident(dst) operator(=) ident(src)
+operator(()string<delimiter(')content(a)delimiter(')>operator(..)string<delimiter(')content(z)delimiter(')>(\))operator(.)ident(each)operator({) ident(dst) operator(=) ident(dst)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((?<=)content(\\A)content(|)char(\\b)content(\))delimiter(/)>operator(+)local_variable(it)operator(,) local_variable(it)operator(.)ident(toUpperCase)operator(()(\)\)) (})
+ident(println) ident(dst)
+comment(// => Dierk König)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// Several libraries exist, e.g.)
+comment(// http://secondstring.sourceforge.net/)
+comment(// http://sourceforge.net/projects/simmetrics/)
+comment(// both support numerous algorithms. Using the second as an example:)
+keyword(import) include(uk.ac.shef.wit.simmetrics.similaritymetrics.*)
+ident(target) operator(=) string<delimiter(')content(balast)delimiter(')>
+ident(candidates) operator(=) string<delimiter(''')content(
+quick
+brown
+fox
+jumped
+over
+the
+lazy
+dog
+ballast
+ballasts
+balustrade
+balustrades
+blast
+blasted
+blaster
+blasters
+blasting
+blasts
+)delimiter(''')>operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(findAll)operator({)local_variable(it)operator(.)ident(trim)operator(()(\)})
+ident(metrics) operator(=) operator([)keyword(new) ident(Levenshtein)operator(()(\))operator(,) keyword(new) ident(MongeElkan)operator(()(\))operator(,) keyword(new) ident(JaroWinkler)operator(()(\))operator(,) keyword(new) ident(Soundex)operator(()(\)])
+keyword(def) method(out)operator(()ident(name)operator(,) ident(results)(\)) operator({)
+ ident(print) ident(name)operator(.)ident(padLeft)operator(()integer(14)(\)) operator(+) string<delimiter(')content( )delimiter(')>operator(;) ident(results)operator(.)ident(each)operator({)ident(print)operator(()local_variable(it)operator(.)ident(padRight)operator(()integer(16)(\)\)})operator(;) ident(println)operator(()(\))
+(})
+keyword(def) method(outr)operator(()ident(name)operator(,) ident(results)(\))operator({)ident(out)operator(()ident(name)operator(,) ident(results)operator(.)ident(collect)operator({)string<delimiter(')delimiter(')>operator(+)operator(()operator(()type(int)(\))operator(()local_variable(it)operator(*)integer(100)(\)\))operator(/)integer(100)(}\)})
+ident(out) operator(()string<delimiter(')content(Word/Metric)delimiter(')>operator(,) ident(metrics)operator(.)ident(collect)operator({)local_variable(it)operator(.)ident(shortDescriptionString)(}) (\))
+ident(candidates)operator(.)ident(each)operator({) ident(w) operator(->) ident(outr)operator(()ident(w)operator(,) ident(metrics)operator(.)ident(collect)operator({) ident(m) operator(->) ident(m)operator(.)ident(getSimilarity)operator(()ident(target)operator(,) ident(w)(\)}) (\)})
+comment(// =>)
+comment(// Word/Metric Levenshtein MongeElkan JaroWinkler Soundex)
+comment(// quick 0 0.11 0 0.66)
+comment(// brown 0.16 0.23 0.5 0.73)
+comment(// fox 0 0.2 0 0.66)
+comment(// jumped 0 0.2 0 0.66)
+comment(// over 0 0.44 0 0.55)
+comment(// the 0 0.33 0 0.55)
+comment(// lazy 0.33 0.5 0.44 0.66)
+comment(// dog 0 0.2 0 0.66)
+comment(// ballast 0.85 0.83 0.96 1)
+comment(// ballasts 0.75 0.83 0.94 0.94)
+comment(// balustrade 0.5 0.93 0.3 0.94)
+comment(// balustrades 0.45 0.93 0.3 0.94)
+comment(// blast 0.83 0.8 0.88 1)
+comment(// blasted 0.57 0.66 0.8 0.94)
+comment(// blaster 0.57 0.66 0.8 0.94)
+comment(// blasters 0.5 0.66 0.77 0.94)
+comment(// blasting 0.5 0.66 0.77 0.94)
+comment(// blasts 0.66 0.66 0.84 0.94)
+comment(// to implement the example, iterate through /usr/dict/words selecting words)
+comment(// where one or a combination of metrics are greater than some threshold)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.14)
+comment(//----------------------------------------------------------------------------------)
+ident(n) operator(=) string<delimiter(")content( 49 here)delimiter(")>
+ident(println) ident(n)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(\\G)content( )delimiter(/)>operator(,)string<delimiter(')content(0)delimiter(')>(\))
+comment(// => 00049 here)
+
+ident(str) operator(=) string<delimiter(")content(3,4,5,9,120)delimiter(")>
+ident(print) string<delimiter(')content(Found numbers:)delimiter(')>
+ident(str)operator(.)ident(eachMatch)operator(()regexp<delimiter(/)content(\\G)content(,?()char(\\d)content(+\))delimiter(/)>(\))operator({) ident(print) string<delimiter(')content( )delimiter(')> operator(+) local_variable(it)operator([)integer(1)(]) (})
+ident(println)operator(()(\))
+comment(// => Found numbers: 3 4 5 9 120)
+
+comment(// Groovy doesn't have the String.pos or a /c re modifier like Perl)
+comment(// But it does have similar functionality. Matcher has start(\) and)
+comment(// end(\) for find the position and Matcher's usePattern(\) allows)
+comment(// you to swap patterns without changing the buffer position)
+ident(text) operator(=) string<delimiter(')content(the year 1752 lost 10 days on the 3rd of September)delimiter(')>
+ident(p) operator(=) operator(~)regexp<delimiter(/)content((?<=)char(\\D)content(\)()char(\\d)content(+\))delimiter(/)>
+ident(m) operator(=) ident(p)operator(.)ident(matcher)operator(()ident(text)(\))
+keyword(while) operator(()ident(m)operator(.)ident(find)operator(()(\)\)) operator({)
+ ident(println) string<delimiter(')content(Found )delimiter(')> operator(+) ident(m)operator(.)ident(group)operator(()(\)) operator(+) string<delimiter(')content( starting at pos )delimiter(')> operator(+) ident(m)operator(.)ident(start)operator(()(\)) operator(+)
+ string<delimiter(')content( and ending at pos )delimiter(')> operator(+) ident(m)operator(.)ident(end)operator(()(\))
+(})
+comment(// now reset pos back to between 1st and 2nd numbers)
+keyword(if) operator(()ident(m)operator(.)ident(find)operator(()integer(16)(\)\)) operator({) ident(println) string<delimiter(')content(Found )delimiter(')> operator(+) ident(m)operator(.)ident(group)operator(()(\)) (})
+comment(// =>)
+comment(// Found 1752 starting at pos 9 and ending at pos 13)
+comment(// Found 10 starting at pos 19 and ending at pos 21)
+comment(// Found 3 starting at pos 34 and ending at pos 35)
+comment(// Found 10)
+
+comment(// Alternatively you can use Scanner in Java 5-7+:)
+ident(p1) operator(=) operator(~)regexp<delimiter(/)content((?<=)char(\\D)content(\)()char(\\d)content(+\))delimiter(/)>
+ident(p2) operator(=) operator(~)regexp<delimiter(/)char(\\S)content(+)delimiter(/)>
+ident(s) operator(=) keyword(new) pre_type(Scanner)operator(()ident(text)(\))
+keyword(while) operator(()operator(()ident(f) operator(=) ident(s)operator(.)ident(findInLine)operator(()ident(p1)(\)\)\)) operator({) ident(println) string<delimiter(')content(Found: )delimiter(')> operator(+) ident(f) (})
+keyword(if) operator(()operator(()ident(f) operator(=) ident(s)operator(.)ident(findInLine)operator(()ident(p2)(\)\)\)) operator({) ident(println) string<delimiter(")content(Found )inline<inline_delimiter($)ident(f)>content( after the last number.)delimiter(")> (})
+comment(// =>)
+comment(// Found: 1752)
+comment(// Found: 10)
+comment(// Found: 3)
+comment(// Found rd after the last number.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.15)
+comment(//----------------------------------------------------------------------------------)
+ident(html) operator(=) string<delimiter(')content(<b><i>this</i> and <i>that</i> are important</b> Oh, <b><i>me too!</i></b>)delimiter(')>
+
+ident(greedyHtmlStripPattern) operator(=) operator(~)regexp<delimiter(/)content((?m\)<.*>)delimiter(/)> comment(// not good)
+ident(nonGreedyHtmlStripPattern) operator(=) operator(~)regexp<delimiter(/)content((?m\)<.*?>)delimiter(/)> comment(// not great)
+ident(simpleNested) operator(=) operator(~)regexp<delimiter(/)content((?mx\)<b><i>(.*?\)<)content(\\/)content(i><)content(\\/)content(b>)delimiter(/)>
+comment(// match BEGIN, then not BEGIN, then END)
+ident(generalPattern) operator(=) operator(~)regexp<delimiter(/)content(BEGIN((?:(?!BEGIN\).\)*\)END)delimiter(/)>
+ident(betterButInefficient1) operator(=) operator(~)regexp<delimiter(/)content((?mx\)<b><i>( (?: (?!<)content(\\/)content(b>|<)content(\\/)content(i>\). \)* \) <)content(\\/)content(i><)content(\\/)content(b>)delimiter(/)>
+ident(betterButInefficient2) operator(=) operator(~)regexp<delimiter(/)content((?mx\)<b><i>( (?: (?!<)content(\\/)content([ib]>\). \)* \) <)content(\\/)content(i><)content(\\/)content(b>)delimiter(/)>
+
+ident(efficientPattern) operator(=) string<delimiter(''')content((?mx\)
+ <b><i>
+ [^<]* # stuff not possibly bad, and not possibly the end.
+ (?:
+ # at this point, we can have '<' if not part of something bad
+ (?! </?[ib]> \) # what we can't have
+ < # okay, so match the '<'
+ [^<]* # and continue with more safe stuff
+ \) *
+ </i></b>
+)delimiter(''')> comment(//')
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.16)
+comment(//----------------------------------------------------------------------------------)
+ident(input) operator(=) string<delimiter(')content(This is a test)content(\\n)content(Test of the duplicate word finder.)content(\\n)delimiter(')>
+ident(dupWordPattern) operator(=) string<delimiter(''')content((?ix\)
+ )content(\\\\)content(b # start at word boundary
+ ()content(\\\\)content(S+\) # find chunk of non-whitespace
+ )content(\\\\)content(b # until a word boundary
+ (
+ )content(\\\\)content(s+ # followed by whitespace
+ )content(\\\\)content(1 # and that same chunk again
+ )content(\\\\)content(b # and a word boundary
+ \) + # one or more times
+)delimiter(''')>
+ident(finder) operator(=) ident(input) operator(=~) ident(dupWordPattern)
+ident(println) string<delimiter(')content(Found duplicate word: )delimiter(')> operator(+) ident(finder)operator([)integer(0)(])operator([)integer(1)(])
+comment(// => Found duplicate word: test)
+
+ident(astr) operator(=) string<delimiter(')content(nobody)delimiter(')>
+ident(bstr) operator(=) string<delimiter(')content(bodysnatcher)delimiter(')>
+ident(m) operator(=) string<delimiter(")inline<inline_delimiter($)ident(astr)>content( )inline<inline_delimiter($)ident(bstr)>delimiter(")> operator(=~) regexp<delimiter(/)content(^()char(\\w)content(+\)()char(\\w)content(+\) )char(\\2)content(()char(\\w)content(+\))content($)delimiter(/)>
+ident(actual) operator(=) string<delimiter(")inline<inline_delimiter(${)ident(m[0][2])inline_delimiter(})>content( overlaps in )inline<inline_delimiter(${)ident(m[0][1])inline_delimiter(})>content(-)inline<inline_delimiter(${)ident(m[0][2])inline_delimiter(})>content(-)inline<inline_delimiter(${)ident(m[0][3])inline_delimiter(})>delimiter(")>
+keyword(assert) ident(actual) operator(==) string<delimiter(')content(body overlaps in no-body-snatcher)delimiter(')>
+
+ident(cap) operator(=) string<delimiter(')content(o)delimiter(')> operator(*) integer(180)
+keyword(while) operator(()ident(m) operator(=) operator(()ident(cap) operator(=~) regexp<delimiter(/)content(^(oo+?\))char(\\1)content(+)content($)delimiter(/)>(\)\)) operator({)
+ ident(p1) operator(=) ident(m)operator([)integer(0)(])operator([)integer(1)(])
+ ident(print) ident(p1)operator(.)ident(size)operator(()(\)) operator(+) string<delimiter(')content( )delimiter(')>
+ ident(cap) operator(=) ident(cap)operator(.)ident(replaceAll)operator(()ident(p1)operator(,)string<delimiter(')content(o)delimiter(')>(\))
+(})
+ident(println) ident(cap)operator(.)ident(size)operator(()(\))
+comment(// => 2 2 3 3 5)
+
+comment(// diophantine)
+comment(// solve for 12x + 15y + 16z = 281, maximizing x)
+keyword(if) operator(()operator(()ident(m) operator(=) operator(()string<delimiter(')content(o)delimiter(')> operator(*) integer(281)(\)) operator(=~) regexp<delimiter(/)content(^(o*\))char(\\1)content({11}(o*\))char(\\2)content({14}(o*\))char(\\3)content({15})content($)delimiter(/)>(\)\)) operator({)
+ ident(x)operator(=)ident(m)operator([)integer(0)(])operator([)integer(1)(])operator(.)ident(size)operator(()(\))operator(;) ident(y)operator(=)ident(m)operator([)integer(0)(])operator([)integer(2)(])operator(.)ident(size)operator(()(\))operator(;) ident(z)operator(=)ident(m)operator([)integer(0)(])operator([)integer(3)(])operator(.)ident(size)operator(()(\))
+ ident(println) string<delimiter(")content(One solution is: x=)inline<inline_delimiter($)ident(x)>content(; y=)inline<inline_delimiter($)ident(y)>content(; z=)inline<inline_delimiter($)ident(z)>delimiter(")>
+(}) keyword(else) ident(println) string<delimiter(")content(No solution.)delimiter(")>
+comment(// => One solution is: x=17; y=3; z=2)
+
+comment(// using different quantifiers:)
+comment(// /^(o+\)\\1{11}(o+\)\\2{14}(o+\)\\3{15}$/)
+comment(// => One solution is: x=17; y=3; z=2)
+
+comment(// /^(o*?\)\\1{11}(o*\)\\2{14}(o*\)\\3{15}$/)
+comment(// => One solution is: x=0; y=7; z=11)
+
+comment(// /^(o+?\)\\1{11}(o*\)\\2{14}(o*\)\\3{15}$/)
+comment(// => One solution is: x=1; y=3; z=14)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.17)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy doesn't currently support x!~y so you must use the !(x=~y\) style)
+
+comment(// alpha OR beta)
+keyword(assert) string<delimiter(')content(alpha)delimiter(')> operator(==~) regexp<delimiter(/)content(alpha|beta)delimiter(/)>
+keyword(assert) string<delimiter(')content(beta)delimiter(')> operator(==~) regexp<delimiter(/)content(alpha|beta)delimiter(/)>
+keyword(assert) string<delimiter(')content(betalpha)delimiter(')> operator(=~) regexp<delimiter(/)content(alpha)delimiter(/)> operator(||) string<delimiter(')content(betalpha)delimiter(')> operator(=~) regexp<delimiter(/)content(beta)delimiter(/)>
+
+comment(// alpha AND beta)
+keyword(assert) operator(!)operator(()string<delimiter(')content(alpha)delimiter(')> operator(=~) regexp<delimiter(/)content((?=.*alpha\)(?=.*beta\))delimiter(/)>(\))
+keyword(assert) string<delimiter(')content(alphabeta)delimiter(')> operator(=~) regexp<delimiter(/)content((?=.*alpha\)(?=.*beta\))delimiter(/)>
+keyword(assert) string<delimiter(')content(betalpha)delimiter(')> operator(=~) regexp<delimiter(/)content((?=.*alpha\)(?=.*beta\))delimiter(/)>
+keyword(assert) string<delimiter(')content(betalpha)delimiter(')> operator(=~) regexp<delimiter(/)content(alpha)delimiter(/)> operator(&&) string<delimiter(')content(betalpha)delimiter(')> operator(=~) regexp<delimiter(/)content(beta)delimiter(/)>
+
+comment(// alpha AND beta, no overlap)
+keyword(assert) string<delimiter(')content(alphabeta)delimiter(')> operator(=~) regexp<delimiter(/)content(alpha.*beta|beta.*alpha)delimiter(/)>
+keyword(assert) operator(!)operator(()string<delimiter(')content(betalpha)delimiter(')> operator(=~) regexp<delimiter(/)content(alpha.*beta|beta.*alpha)delimiter(/)>(\))
+
+comment(// NOT beta)
+keyword(assert) string<delimiter(')content(alpha gamma)delimiter(')> operator(=~) regexp<delimiter(/)content(^(?:(?!beta\).\)*)content($)delimiter(/)>
+keyword(assert) operator(!)operator(()string<delimiter(')content(alpha beta gamma)delimiter(')> operator(=~) regexp<delimiter(/)content(^(?:(?!beta\).\)*)content($)delimiter(/)>(\))
+
+comment(// NOT bad BUT good)
+keyword(assert) operator(!)operator(()string<delimiter(')content(GOOD and BAD)delimiter(')> operator(=~) regexp<delimiter(/)content((?=(?:(?!BAD\).\)*)content($)content(\)GOOD)delimiter(/)>(\))
+keyword(assert) operator(!)operator(()string<delimiter(')content(BAD)delimiter(')> operator(=~) regexp<delimiter(/)content((?=(?:(?!BAD\).\)*)content($)content(\)GOOD)delimiter(/)>(\))
+keyword(assert) operator(!)operator(()string<delimiter(')content(WORSE)delimiter(')> operator(=~) regexp<delimiter(/)content((?=(?:(?!BAD\).\)*)content($)content(\)GOOD)delimiter(/)>(\))
+keyword(assert) string<delimiter(')content(GOOD)delimiter(')> operator(=~) regexp<delimiter(/)content((?=(?:(?!BAD\).\)*)content($)content(\)GOOD)delimiter(/)>
+
+comment(// minigrep could be done as a one-liner as follows)
+comment(// groovy -p -e "if (line =~ /pat/\) return line" datafile)
+
+ident(string) operator(=) string<delimiter(')content(labelled)delimiter(')>
+keyword(assert) ident(string) operator(=~) regexp<delimiter(/)content(^(?=.*bell\)(?=.*lab\))delimiter(/)>
+keyword(assert) ident(string) operator(=~) regexp<delimiter(/)content(bell)delimiter(/)> operator(&&) ident(string) operator(=~) string<delimiter(')content(lab)delimiter(')>
+ident(fakeAddress) operator(=) string<delimiter(")content(blah bell blah )delimiter(")>
+ident(murrayHillRegex) operator(=) string<delimiter(''')content((?x\)
+ ^ # start of string
+ (?= # zero-width lookahead
+ .* # any amount of intervening stuff
+ bell # the desired bell string
+ \) # rewind, since we were only looking
+ (?= # and do the same thing
+ .* # any amount of intervening stuff
+ lab # and the lab part
+ \)
+)delimiter(''')>
+keyword(assert) ident(string) operator(=~) ident(murrayHillRegex)
+keyword(assert) operator(!)operator(()ident(fakeAddress) operator(=~) ident(murrayHillRegex)(\))
+
+comment(// eliminate overlapping)
+keyword(assert) operator(!)operator(()ident(string) operator(=~) regexp<delimiter(/)content((?:^.*bell.*lab\)|(?:^.*lab.*bell\))delimiter(/)>(\))
+
+ident(brandRegex) operator(=) string<delimiter(''')content((?x\)
+ (?: # non-capturing grouper
+ ^ .*? # any amount of stuff at the front
+ bell # look for a bell
+ .*? # followed by any amount of anything
+ lab # look for a lab
+ \) # end grouper
+ | # otherwise, try the other direction
+ (?: # non-capturing grouper
+ ^ .*? # any amount of stuff at the front
+ lab # look for a lab
+ .*? # followed by any amount of anything
+ bell # followed by a bell
+ \) # end grouper
+)delimiter(''')>
+keyword(assert) operator(!)operator(()ident(string) operator(=~) ident(brandRegex)(\))
+
+ident(map) operator(=) string<delimiter(')content(the great baldo)delimiter(')>
+
+keyword(assert) ident(map) operator(=~) regexp<delimiter(/)content(^(?:(?!waldo\).\)*)content($)delimiter(/)>
+ident(noWaldoRegex) operator(=) string<delimiter(''')content((?x\)
+ ^ # start of string
+ (?: # non-capturing grouper
+ (?! # look ahead negation
+ waldo # is he ahead of us now?
+ \) # is so, the negation failed
+ . # any character (cuzza /s\)
+ \) * # repeat that grouping 0 or more
+ )content($)content( # through the end of the string
+)delimiter(''')>
+keyword(assert) ident(map) operator(=~) ident(noWaldoRegex)
+
+comment(// on unix systems use: realFakedInput = 'w'.process(\).text)
+ident(fakedInput) operator(=) string<delimiter(''')content(
+ 7:15am up 206 days, 13:30, 4 users, load average: 1.04, 1.07, 1.04
+USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
+tchrist tty1 5:16pm 36days 24:43 0.03s xinit
+tchrist tty2 5:19pm 6days 0.43s 0.43s -tcsh
+tchrist ttyp0 chthon 7:58am 3days 23.44s 0.44s -tcsh
+gnat ttyS4 coprolith 2:01pm 13:36m 0.30s 0.30s -tcsh
+)delimiter(''')>operator(.)ident(trim)operator(()(\)) operator(+) string<delimiter(')content(\\n)delimiter(')>
+
+keyword(def) method(miniGrepMethod)operator(()ident(input)(\)) operator({)
+ ident(input)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(findAll)operator({)local_variable(it) operator(=~) string<delimiter(')content(^(?!.*ttyp\).*tchrist)delimiter(')>(})
+(})
+keyword(assert) ident(miniGrepMethod)operator(()ident(fakedInput)(\))operator(.)ident(size)operator(()(\)) operator(==) integer(2)
+
+ident(findUserRegex) operator(=) string<delimiter(''')content((?xm\)
+ ^ # anchored to the start
+ (?! # zero-width look-ahead assertion
+ .* # any amount of anything (faster than .*?\)
+ ttyp # the string you don't want to find
+ \) # end look-ahead negation; rewind to start
+ .* # any amount of anything (faster than .*?\)
+ tchrist # now try to find Tom
+)delimiter(''')>
+keyword(assert) operator(()ident(fakedInput) operator(=~) ident(findUserRegex)(\))operator(.)ident(count) operator(==) integer(2)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.18)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy uses Unicode character encoding)
+comment(// special care needs to be taken when using unicode because of the different)
+comment(// byte lengths, e.g. à can be encoded as two bytes \\u0061\\u0300 and is also)
+comment(// supported in legacy character sets by a single character \\u00E0. To Match)
+comment(// this character, you can't use any of /./, /../, /a/, /\\u00E0/, /\\u0061/\\u0300)
+comment(// or /\\pL/. The correct way is to use /X (not currently supported\) or one)
+comment(// of /\\pL/\\pM*/ to ensure that it is a letter or /\\PM\\pM*/ when you just want)
+comment(// to combine multicharacter sequences and don't care whether it is a letter)
+keyword(def) method(checkUnicode)operator(()ident(s)(\)) operator({)
+ ident(println) ident(s) operator(+) string<delimiter(')content( is of size )delimiter(')> operator(+) ident(s)operator(.)ident(size)operator(()(\))
+ ident(println) string<delimiter(')content(Exactly matches /./ )delimiter(')> operator(+) operator(()ident(s) operator(==~) regexp<delimiter(/)content(.)delimiter(/)>(\))
+ ident(println) string<delimiter(')content(Exactly matches /../ )delimiter(')> operator(+) operator(()ident(s) operator(==~) regexp<delimiter(/)content(..)delimiter(/)>(\))
+ ident(println) string<delimiter(')content(Exactly matches /a/ )delimiter(')> operator(+) operator(()ident(s) operator(==~) regexp<delimiter(/)content(a)delimiter(/)>(\))
+ ident(println) string<delimiter(')content(Exactly matches /)char(\\\\)content(u00E0/ )delimiter(')> operator(+) operator(()ident(s) operator(==~) regexp<delimiter(/)char(\\u00E0)delimiter(/)>(\))
+ ident(println) string<delimiter(')content(Exactly matches /)char(\\\\)content(u0061)char(\\\\)content(u0300/ )delimiter(')> operator(+) operator(()ident(s) operator(==~) regexp<delimiter(/)char(\\u0061)char(\\u0300)delimiter(/)>(\))
+ ident(println) string<delimiter(')content(Exactly matches /)char(\\\\)content(pL/ )delimiter(')> operator(+) operator(()ident(s) operator(==~) regexp<delimiter(/)content(\\p)content(L)delimiter(/)>(\))
+ ident(println) string<delimiter(')content(Exactly matches /)char(\\\\)content(pL)char(\\\\)content(pM*/ )delimiter(')> operator(+) operator(()ident(s) operator(==~) regexp<delimiter(/)content(\\p)content(L)content(\\p)content(M*)delimiter(/)>(\))
+ ident(println) string<delimiter(')content(Exactly matches /)char(\\\\)content(PM)char(\\\\)content(pM*/ )delimiter(')> operator(+) operator(()ident(s) operator(==~) regexp<delimiter(/)content(\\P)content(M)content(\\p)content(M*)delimiter(/)>(\))
+(})
+ident(checkUnicode)operator(()string<delimiter(')content(à)delimiter(')>(\))
+ident(checkUnicode)operator(()string<delimiter(')content(\\u0061)content(\\u0300)delimiter(')>(\))
+ident(checkUnicode)operator(()string<delimiter(')content(\\u00E0)delimiter(')>(\))
+comment(// =>)
+comment(// à is of size 1)
+comment(// Exactly matches /./ true)
+comment(// Exactly matches /../ false)
+comment(// Exactly matches /a/ false)
+comment(// Exactly matches /\\u00E0/ true)
+comment(// Exactly matches /\\u0061\\u0300/ false)
+comment(// Exactly matches /\\pL/ true)
+comment(// Exactly matches /\\pL\\pM*/ true)
+comment(// Exactly matches /\\PM\\pM*/ true)
+comment(// a? is of size 2)
+comment(// Exactly matches /./ false)
+comment(// Exactly matches /../ true)
+comment(// Exactly matches /a/ false)
+comment(// Exactly matches /\\u00E0/ false)
+comment(// Exactly matches /\\u0061\\u0300/ true)
+comment(// Exactly matches /\\pL/ false)
+comment(// Exactly matches /\\pL\\pM*/ true)
+comment(// Exactly matches /\\PM\\pM*/ true)
+comment(// à is of size 1)
+comment(// Exactly matches /./ true)
+comment(// Exactly matches /../ false)
+comment(// Exactly matches /a/ false)
+comment(// Exactly matches /\\u00E0/ true)
+comment(// Exactly matches /\\u0061\\u0300/ false)
+comment(// Exactly matches /\\pL/ true)
+comment(// Exactly matches /\\pL\\pM*/ true)
+comment(// Exactly matches /\\PM\\pM*/ true)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.19)
+comment(//----------------------------------------------------------------------------------)
+comment(// The Perl Cookbook categorizes this as a hard problem ... mostly for)
+comment(// reasons not related to the actual regex - but with a 60-line regex)
+comment(// perhaps there are some issues with that too. Further details:)
+comment(// http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/ckaddr.gz)
+
+ident(simpleCommentStripper) operator(=) regexp<delimiter(/)content(\\()content([^(\)]*)content(\\\))delimiter(/)>
+ident(println) string<delimiter(')content(Book Publishing <marketing@books.com> (We will spam you\))delimiter(')>operator(.)ident(replaceAll)operator(()ident(simpleCommentStripper)operator(,) string<delimiter(')delimiter(')>(\))
+comment(// => Book Publishing <marketing@books.com>)
+
+comment(// inspired by the fact that domain names can contain any foreign character these days)
+ident(modern) operator(=) regexp<delimiter(/)content(^.+@[^)content(\\.)content(].*)content(\\.)content([a-z]{2,}>?)content($)delimiter(/)>
+
+comment(// .Net )
+ident(lenient) operator(=) regexp<delimiter(/)char(\\w)content(+([-+.])char(\\w)content(+\)*@)char(\\w)content(+([-.])char(\\w)content(+\)*)content(\\.)char(\\w)content(+([-.])char(\\w)content(+\)*)delimiter(/)>
+
+comment(// a little more checking)
+ident(strict) operator(=) regexp<delimiter(/)content(^[_a-zA-Z0-9- <]+()content(\\.)content([_a-zA-Z0-9- <]+\)*@[a-zA-Z0-9-]+()content(\\.)content([a-zA-Z0-9-]+\)*)content(\\.)delimiter(/)> operator(+)
+ regexp<delimiter(/)content((([0-9]{1,3}\)|([a-zA-Z]{2,3}\)|(aero|coop|info|museum|name\)\)>?)content($)delimiter(/)>
+
+ident(addresses) operator(=) operator([)string<delimiter(')content(someuser@somehost.com)delimiter(')>operator(,)
+ string<delimiter(')content(Book Publishing <marketing@books.com>)delimiter(')>(])
+ident(addresses)operator(.)ident(each)operator({)
+ keyword(assert) local_variable(it) operator(=~) ident(lenient)
+ keyword(assert) local_variable(it) operator(=~) ident(strict)
+ keyword(assert) local_variable(it) operator(=~) ident(modern)
+(})
+
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.20)
+comment(//----------------------------------------------------------------------------------)
+keyword(def) method(findAction)operator(()ident(ans)(\)) operator({)
+ keyword(def) ident(re) operator(=) string<delimiter(')content((?i\)^)delimiter(')> operator(+) pre_type(Pattern)operator(.)ident(quote)operator(()ident(ans)(\))
+ keyword(if) operator(()string<delimiter(")content(SEND)delimiter(")> operator(=~) ident(re)(\)) ident(println) string<delimiter(")content(Action is send)delimiter(")>
+ keyword(else) keyword(if) operator(()string<delimiter(")content(STOP)delimiter(")> operator(=~) ident(re)(\)) ident(println) string<delimiter(")content(Action is stop)delimiter(")>
+ keyword(else) keyword(if) operator(()string<delimiter(")content(ABORT)delimiter(")> operator(=~) ident(re)(\)) ident(println) string<delimiter(")content(Action is abort)delimiter(")>
+ keyword(else) keyword(if) operator(()string<delimiter(")content(EDIT)delimiter(")> operator(=~) ident(re)(\)) ident(println) string<delimiter(")content(Action is edit)delimiter(")>
+ keyword(else) ident(println) string<delimiter(')content(No Match)delimiter(')>
+(})
+ident(findAction)operator(()string<delimiter(')content(edit something)delimiter(')>(\))
+comment(// => No Match)
+ident(findAction)operator(()string<delimiter(')content(edit)delimiter(')>(\))
+comment(// => Action is edit)
+ident(findAction)operator(()string<delimiter(')content(se)delimiter(')>(\))
+comment(// => Action is send)
+ident(findAction)operator(()string<delimiter(')content(e)delimiter(')>(\))
+comment(// => Action is edit)
+
+keyword(def) method(buildAbbrev)operator(()ident(words)(\)) operator({)
+ keyword(def) ident(table) operator(=) keyword(new) pre_type(TreeMap)operator(()(\))
+ ident(words)operator(.)ident(each)operator({) ident(w) operator(->)
+ operator(()integer(0)operator(..<)ident(w)operator(.)ident(size)operator(()(\)\))operator(.)ident(each) operator({) ident(n) operator(->)
+ keyword(if) operator(()operator(!)operator(()ident(words) operator(-) ident(w)(\))operator(.)ident(any)operator({)
+ local_variable(it)operator(.)ident(size)operator(()(\)) operator(>=) ident(n)operator(+)integer(1) operator(&&) local_variable(it)operator([)integer(0)operator(..)ident(n)(]) operator(==) ident(w)operator([)integer(0)operator(..)ident(n)(])
+ (}\)) ident(table)operator([)ident(w)operator([)integer(0)operator(..)ident(n)(]]) operator(=) ident(w)
+ (})
+ (})
+ ident(table)
+(})
+ident(println) ident(buildAbbrev)operator(()string<delimiter(')content(send stop abort edit)delimiter(')>operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))operator(.)ident(toList)operator(()(\)\))
+comment(// => ["a":"abort", "ab":"abort", "abo":"abort", "abor":"abort", "abort":"abort",)
+comment(// "e":"edit", "ed":"edit", "edi":"edit", "edit":"edit", "se":"send", "sen":"send",)
+comment(// "send":"send", "st":"stop", "sto":"stop", "stop":"stop"])
+
+comment(// miniShellScript:)
+comment(// dummy methods)
+keyword(def) method(invokeEditor)operator(()(\)) operator({) ident(println) string<delimiter(")content(invoking editor)delimiter(")> (})
+keyword(def) method(deliverMessage)operator(()(\)) operator({) ident(println) string<delimiter(")content(delivering message at )delimiter(")> operator(+) keyword(new) pre_type(Date)operator(()(\)) (})
+ident(actions) operator(=) operator([)
+ key(edit)operator(:) local_variable(this)operator(.)operator(&)ident(invokeEditor)operator(,)
+ key(send)operator(:) local_variable(this)operator(.)operator(&)ident(deliverMessage)operator(,)
+ key(list)operator(:) operator({) ident(println) pre_type(Runtime)operator(.)ident(runtime)operator(.)ident(freeMemory)operator(()(\)) (})operator(,)
+ key(abort)operator(:) operator({) pre_type(System)operator(.)ident(exit)operator(()integer(0)(\)) (})operator(,)
+ key(unknown)operator(:) operator({) ident(println) string<delimiter(")content(Unknown Command)delimiter(")>(})
+(])
+
+ident(table) operator(=) ident(buildAbbrev)operator(()ident(actions)operator(.)ident(keySet)operator(()(\))operator(.)ident(toList)operator(()(\)\))
+ident(prompt) operator(=) string<delimiter(')content(\\n)content(> )delimiter(')>
+ident(print) string<delimiter(')content(Enter Commands: edit send list abort)delimiter(')> operator(+) ident(prompt)
+keyword(new) pre_type(BufferedReader)operator(()keyword(new) pre_type(InputStreamReader)operator(()pre_type(System)operator(.)ident(in)(\)\))operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ keyword(def) ident(idx) operator(=) operator(()ident(table)operator(.)ident(containsKey)operator(()ident(line)(\)\)) operator(?) ident(table)operator([)ident(line)(]) operator(:) string<delimiter(')content(unknown)delimiter(')>
+ ident(actions)operator([)ident(idx)(])operator(()(\))
+ ident(print) ident(prompt)
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.21)
+comment(//----------------------------------------------------------------------------------)
+comment(//% gunzip -c ~/mail/archive.gz | urlify > archive.urlified)
+comment(//% urlify ~/mail/*.inbox > ~/allmail.urlified)
+
+ident(urls) operator(=) string<delimiter(')content((https?|telnet|gopher|file|wais|ftp|mail\))delimiter(')>
+ident(ltrs) operator(=) regexp<delimiter(/)char(\\w)delimiter(/)>
+ident(gunk) operator(=) regexp<delimiter(/)content(\\#)content(\\/)content(~:.?+=&%@!)content(\\-)delimiter(/)>
+ident(punc) operator(=) regexp<delimiter(/)content(.:?)content(\\-)delimiter(/)>
+ident(doll) operator(=) regexp<delimiter(/)content($)delimiter(/)>
+ident(all) operator(=) regexp<delimiter(/)inline<inline_delimiter($)ident(ltrs)>inline<inline_delimiter($)ident(gunk)>inline<inline_delimiter($)ident(punc)>delimiter(/)>
+
+ident(findUrls) operator(=) string<delimiter(""")content((?ix\)
+ )content(\\\\)content(b # start at word boundary
+ ( # begin group 1 {
+ )inline<inline_delimiter($)ident(urls)>content( : # need resource and a colon
+ [)inline<inline_delimiter($)ident(all)>content(] +? # followed by on or more of any valid
+ # character, but be conservative and
+ # take only what you need to...
+ \) # end group 1 }
+ (?= # look-ahead non-consumptive assertion
+ [)inline<inline_delimiter($)ident(punc)>content(]* # either 0 or more punctuation
+ [^)inline<inline_delimiter($)ident(all)>content(] # followed by a non-url character
+ | # or else
+ )inline<inline_delimiter($)ident(doll)>content( # then end of the string
+ \)
+)delimiter(""")>
+
+ident(input) operator(=) string<delimiter(''')content(
+If you find a typo on http://groovy.codehaus.org please
+send an email to mail:spelling.pedant@codehaus.org
+)delimiter(''')>
+
+ident(println) ident(input)operator(.)ident(replaceAll)operator(()ident(findUrls)operator(,)string<delimiter(')content(<a href=")content($)content(1">)content($)content(1</a>)delimiter(')>(\))
+comment(// =>)
+comment(// If you find a typo on <a href="http://groovy.codehaus.org">http://groovy.codehaus.org</a> please)
+comment(// send an email to <a href="mail:spelling.pedant@codehaus.org">mail:spelling.pedant@codehaus.org</a>)
+
+comment(// urlifyScript:)
+doctype(#!/usr/bin/groovy)
+comment(// urlify - wrap HTML links around URL-like constructs)
+comment(// definitions from above)
+ident(args)operator(.)ident(each)operator({) ident(file) operator(->)
+ keyword(new) pre_type(File)operator(()ident(file)(\))operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ ident(println) ident(line)operator(.)ident(replaceAll)operator(()ident(findUrls)operator(,)string<delimiter(')content(<a href=")content($)content(1">)content($)content(1</a>)delimiter(')>(\))
+ (})
+(})
+
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.22)
+comment(//----------------------------------------------------------------------------------)
+comment(// @@INCOMPLETE@@)
+comment(// @@INCOMPLETE@@)
+
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_6.23)
+comment(//----------------------------------------------------------------------------------)
+ident(romans) operator(=) regexp<delimiter(/)content((?i\)^m*(d?c{0,3}|c[dm]\)(l?x{0,3}|x[lc]\)(v?i{0,3}|i[vx]\))content($)delimiter(/)>
+keyword(assert) string<delimiter(')content(cmxvi)delimiter(')> operator(=~) ident(romans)
+comment(// can't have tens before 1000s (M\) or 100s (C\) after 5s (V\))
+keyword(assert) operator(!)operator(()string<delimiter(')content(xmvci)delimiter(')> operator(=~) ident(romans)(\))
+
+comment(// swap first two words)
+keyword(assert) string<delimiter(')content(the words)delimiter(')>operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(()char(\\S)content(+\)()char(\\s)content(+\)()char(\\S)content(+\))delimiter(/)>operator(,) string<delimiter(')content($)content(3)content($)content(2)content($)content(1)delimiter(')>(\)) operator(==) string<delimiter(')content(words the)delimiter(')>
+
+comment(// extract keyword and value)
+ident(m) operator(=) string<delimiter(')content(k=v)delimiter(')> operator(=~) regexp<delimiter(/)content(()char(\\w)content(+\))char(\\s)content(*=)char(\\s)content(*(.*\))char(\\s)content(*)content($)delimiter(/)>
+keyword(assert) ident(m)operator(.)ident(matches)operator(()(\))
+keyword(assert) ident(m)operator([)integer(0)(])operator([)integer(1)(]) operator(==) string<delimiter(')content(k)delimiter(')>
+keyword(assert) ident(m)operator([)integer(0)(])operator([)integer(2)(]) operator(==) string<delimiter(')content(v)delimiter(')>
+
+ident(hasAtLeastSize) operator(=) operator({) ident(n) operator(->) regexp<delimiter(/)content(.{)inline<inline_delimiter($)ident(n)>content(,})delimiter(/)> (})
+keyword(assert) string<delimiter(')content(abcdefghijklmnopqrstuvwxyz)delimiter(')> operator(=~) ident(hasAtLeastSize)operator(()integer(20)(\))
+
+comment(// MM/DD/YY HH:MM:SS (lenient - doesn't check HH > 23 etc\))
+ident(d) operator(=) regexp<delimiter(/)char(\\d)content(+)delimiter(/)>
+ident(datetime) operator(=) string<delimiter(")content(()inline<inline_delimiter($)ident(d)>content(\)/()inline<inline_delimiter($)ident(d)>content(\)/()inline<inline_delimiter($)ident(d)>content(\) ()inline<inline_delimiter($)ident(d)>content(\):()inline<inline_delimiter($)ident(d)>content(\):()inline<inline_delimiter($)ident(d)>content(\))delimiter(")>
+keyword(assert) string<delimiter(')content(04/05/2006 10:26:59)delimiter(')> operator(=~) ident(datetime)
+
+ident(orig) operator(=) string<delimiter(')content(/usr/bin/vi)delimiter(')>
+ident(expected) operator(=) string<delimiter(')content(/usr/local/bin/vi)delimiter(')>
+ident(orig)operator(.)ident(replaceAll)operator(()string<delimiter(')content(/usr/bin)delimiter(')>operator(,)string<delimiter(')content(/usr/local/bin)delimiter(')>(\)) operator(==) ident(expected)
+
+ident(escapeSequenceRegex) operator(=) regexp<delimiter(/)content(%([0-9A-Fa-f][0-9A-Fa-f]\))delimiter(/)>
+ident(convertEscapeToChar) operator(=) operator({) pre_type(Object)type([]) ident(ch) operator(->) keyword(new) pre_type(Character)operator(()operator(()type(char)(\))pre_type(Integer)operator(.)ident(parseInt)operator(()ident(ch)operator([)integer(1)(])operator(,)integer(16)(\)\)) (})
+keyword(assert) string<delimiter(')content(abc%3cdef)delimiter(')>operator(.)ident(replaceAll)operator(()ident(escapeSequenceRegex)operator(,) ident(convertEscapeToChar)(\)) operator(==) string<delimiter(')content(abc<def)delimiter(')>
+
+ident(commentStripper) operator(=) string<delimiter(''')content((?xms\)
+ /)content(\\\\)content(* # Match the opening delimiter
+ .* # Match a minimal number of characters */
+ )content(\\\\)content(*/ # Match the closing delimiter
+)delimiter(''')>
+
+ident(input) operator(=) string<delimiter(''')content(
+a line
+/*
+some comment
+*/
+another line
+)delimiter(''')>
+ident(expected) operator(=) string<delimiter(''')content(
+a line
+
+another line
+)delimiter(''')>
+
+keyword(assert) ident(input)operator(.)ident(replaceAll)operator(()ident(commentStripper)operator(,)string<delimiter(')delimiter(')>(\)) operator(==) ident(expected)
+
+comment(// emulate s.trim(\))
+keyword(assert) string<delimiter(')content( x y )delimiter(')>operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(^)char(\\s)content(+)delimiter(/)>operator(,) string<delimiter(')delimiter(')>(\))operator(.)ident(replaceAll)operator(()regexp<delimiter(/)char(\\s)content(+)content($)delimiter(/)>operator(,) string<delimiter(')delimiter(')>(\)) operator(==) string<delimiter(')content(x y)delimiter(')>
+
+comment(// convert \\\\n into \\n)
+keyword(assert) operator(()regexp<delimiter(/)content(a)char(\\n)content(b)delimiter(/)>operator(.)ident(replaceAll)operator(()regexp<delimiter(/)char(\\\\)content(n)delimiter(/)>operator(,)string<delimiter(")char(\\n)delimiter(")>(\)) operator(==) string<delimiter(')content(a)content(\\n)content(b)delimiter(')>(\))
+
+comment(// remove package symbol (Groovy/Java doesn't use this as package symbol\))
+keyword(assert) string<delimiter(')content(A::B)delimiter(')>operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(^.*::)delimiter(/)>operator(,) string<delimiter(')delimiter(')>(\)) operator(==) string<delimiter(')content(B)delimiter(')>
+
+comment(// match IP Address (requires leading 0's\))
+ident(ipregex) operator(=) regexp<delimiter(/)content(^([01]?)char(\\d)char(\\d)content(|2[0-4])char(\\d)content(|25[0-5]\))content(\\.)content(([01]?)char(\\d)char(\\d)content(|2[0-4])char(\\d)content(|25[0-5]\))content(\\.)delimiter(/)> operator(+)
+ regexp<delimiter(/)content(([01]?)char(\\d)char(\\d)content(|2[0-4])char(\\d)content(|25[0-5]\))content(\\.)content(([01]?)char(\\d)char(\\d)content(|2[0-4])char(\\d)content(|25[0-5]\))content($)delimiter(/)>
+keyword(assert) operator(!)operator(()string<delimiter(')content(123.456.789)delimiter(')> operator(=~) ident(ipregex)(\))
+keyword(assert) string<delimiter(')content(192.168.000.001)delimiter(')> operator(=~) ident(ipregex)
+
+comment(// extract basename)
+keyword(assert) string<delimiter(')content(c:/usr/temp.txt)delimiter(')>operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(^.*)content(\\/)content({1})delimiter(/)>operator(,) string<delimiter(')delimiter(')>(\)) operator(==) string<delimiter(')content(temp.txt)delimiter(')>
+
+ident(termcap) operator(=) string<delimiter(')content(:co#80:li#24:)delimiter(')>
+ident(m) operator(=) operator(()ident(termcap) operator(=~) regexp<delimiter(/)content(:co)content(\\#)content(()char(\\d)content(+\):)delimiter(/)>(\))
+keyword(assert) ident(m)operator(.)ident(count) operator(==) integer(1)
+keyword(assert) ident(m)operator([)integer(0)(])operator([)integer(1)(]) operator(==) string<delimiter(')content(80)delimiter(')>
+
+keyword(assert) string<delimiter(')content(cmd c:/tmp/junk.txt)delimiter(')>operator(.)ident(replaceAll)operator(()operator(/) error(\\)ident(S)operator(+)error(\\)regexp<delimiter(/)content({1})delimiter(/)>operator(,) string<delimiter(')content( )delimiter(')>(\)) operator(==) string<delimiter(')content(cmd junk.txt)delimiter(')>
+
+ident(os) operator(=) pre_type(System)operator(.)ident(getProperty)operator(()string<delimiter(')content(os.name)delimiter(')>(\))
+ident(println) string<delimiter(')content(Is Linux? )delimiter(')> operator(+) operator(()ident(os) operator(==~) regexp<delimiter(/)content((?i\)linux.*)delimiter(/)>(\))
+ident(println) string<delimiter(')content(Is Windows? )delimiter(')> operator(+) operator(()ident(os) operator(==~) regexp<delimiter(/)content((?i\)windows.*)delimiter(/)>(\))
+ident(println) string<delimiter(')content(Is Mac? )delimiter(')> operator(+) operator(()ident(os) operator(==~) regexp<delimiter(/)content((?i\)mac.*)delimiter(/)>(\))
+
+comment(// join multiline sting)
+ident(multi) operator(=) string<delimiter(''')content(
+This is
+ a test
+)delimiter(''')>operator(.)ident(trim)operator(()(\))
+keyword(assert) ident(multi)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((?m\))char(\\n)char(\\s)content(+)delimiter(/)>operator(,) string<delimiter(')content( )delimiter(')>(\)) operator(==) string<delimiter(')content(This is a test)delimiter(')>
+
+comment(// nums in string)
+ident(string) operator(=) string<delimiter(')content(The 5th test was won today by 10 wickets after 10.5 overs)delimiter(')>
+ident(nums) operator(=) ident(string) operator(=~) regexp<delimiter(/)content(()char(\\d)content(+)content(\\.)content(?)char(\\d)content(*|)content(\\.)char(\\d)content(+\))delimiter(/)>
+keyword(assert) operator(()integer(0)operator(..<)ident(nums)operator(.)ident(count)(\))operator(.)ident(collect)operator({) ident(nums)operator([)local_variable(it)(])operator([)integer(1)(]) (})operator(.)ident(join)operator(()string<delimiter(')content( )delimiter(')>(\)) operator(==) string<delimiter(')content(5 10 10.5)delimiter(')>
+
+comment(// capitalize words)
+ident(words) operator(=) string<delimiter(')content(the Capital words ARE hiding)delimiter(')>
+ident(capwords) operator(=) ident(words) operator(=~) regexp<delimiter(/)content(()char(\\b)content(\\p)content({Upper}+)char(\\b)content(\))delimiter(/)>
+keyword(assert) operator(()integer(0)operator(..<)ident(capwords)operator(.)ident(count)(\))operator(.)ident(collect)operator({) ident(capwords)operator([)local_variable(it)(])operator([)integer(1)(]) (})operator(.)ident(join)operator(()string<delimiter(')content( )delimiter(')>(\)) operator(==) string<delimiter(')content(ARE)delimiter(')>
+
+ident(lowords) operator(=) ident(words) operator(=~) regexp<delimiter(/)content(()char(\\b)content(\\p)content({Lower}+)char(\\b)content(\))delimiter(/)>
+keyword(assert) operator(()integer(0)operator(..<)ident(lowords)operator(.)ident(count)(\))operator(.)ident(collect)operator({) ident(lowords)operator([)local_variable(it)(])operator([)integer(1)(]) (})operator(.)ident(join)operator(()string<delimiter(')content( )delimiter(')>(\)) operator(==) string<delimiter(')content(the words hiding)delimiter(')>
+
+ident(capWords) operator(=) ident(words) operator(=~) regexp<delimiter(/)content(()char(\\b)content(\\p)content({Upper})content(\\p)content({Lower}*)char(\\b)content(\))delimiter(/)>
+keyword(assert) operator(()integer(0)operator(..<)ident(capWords)operator(.)ident(count)(\))operator(.)ident(collect)operator({) ident(capWords)operator([)local_variable(it)(])operator([)integer(1)(]) (})operator(.)ident(join)operator(()string<delimiter(')content( )delimiter(')>(\)) operator(==) string<delimiter(')content(Capital)delimiter(')>
+
+ident(input) operator(=) string<delimiter(''')content(
+If you find a typo on <a href="http://groovy.codehaus.org">http://groovy.codehaus.org</a> please
+send an email to <a href="mail:spelling.pedant@codehaus.org">mail:spelling.pedant@codehaus.org</a>
+)delimiter(''')>
+
+ident(linkRegex) operator(=) regexp<delimiter(/)content((?im\)<A[^>]+?HREF)char(\\s)content(*=)char(\\s)content(*["']?([^'" >]+?\)[ '"]?>)delimiter(/)> comment(//')
+ident(links) operator(=) ident(input) operator(=~) ident(linkRegex)
+operator(()integer(0)operator(..<)ident(links)operator(.)ident(count)(\))operator(.)ident(each)operator({) ident(println) ident(links)operator([)local_variable(it)(])operator([)integer(1)(]) (})
+comment(// =>)
+comment(// http://groovy.codehaus.org)
+comment(// mail:spelling.pedant@codehaus.org)
+
+comment(// find middle initial if any)
+ident(m) operator(=) string<delimiter(')content(Lee Harvey Oswald)delimiter(')> operator(=~) regexp<delimiter(/)content(^)char(\\S)content(+)char(\\s)content(+()char(\\S)content(\))char(\\S)content(*)char(\\s)content(+)char(\\S)delimiter(/)>
+ident(initial) operator(=) ident(m)operator(.)ident(count) operator(?) ident(m)operator([)integer(0)(])operator([)integer(1)(]) operator(:) string<delimiter(")delimiter(")>
+keyword(assert) ident(initial) operator(==) string<delimiter(')content(H)delimiter(')>
+
+comment(// inch marks to quotes)
+ident(println) string<delimiter(')content(I said "Hello" to you.)delimiter(')>operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content("([^"]*\)")delimiter(/)>operator(,) regexp<delimiter(/)content(``)content($)content(1'')delimiter(/)>(\)) comment(//")
+comment(// => I said ``Hello'' to you.)
+
+comment(// extract sentences (2 spaces or newline after punctuation\))
+ident(input) operator(=) string<delimiter(''')content(
+Is this a sentence?
+Yes! And so
+is this. And the fourth.
+)delimiter(''')>
+ident(sentences) operator(=) type([])
+ident(strip) operator(=) ident(input)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(()content(\\p)content({Punct}\))char(\\n)delimiter(/)>operator(,) string<delimiter(')content($)content(1 )delimiter(')>(\))operator(.)ident(replaceAll)operator(()regexp<delimiter(/)char(\\n)delimiter(/)>operator(,) string<delimiter(')content( )delimiter(')>(\))operator(.)ident(replaceAll)operator(()operator(/) operator({)integer(3)operator(,)(})operator(/)operator(,)string<delimiter(')content( )delimiter(')>(\))
+ident(m) operator(=) ident(strip) operator(=~) regexp<delimiter(/)content(()char(\\S)content(.*?)content(\\p)content({Punct}\)(?= |)content(\\Z)content(\))delimiter(/)>
+operator(()integer(0)operator(..<)ident(m)operator(.)ident(count)(\))operator(.)ident(each)operator({) ident(sentences) operator(+=) ident(m)operator([)local_variable(it)(])operator([)integer(1)(]) (})
+keyword(assert) ident(sentences) operator(==) operator([)string<delimiter(")content(Is this a sentence?)delimiter(")>operator(,) string<delimiter(")content(Yes!)delimiter(")>operator(,) string<delimiter(")content(And so is this.)delimiter(")>operator(,) string<delimiter(")content(And the fourth.)delimiter(")>(])
+
+comment(// YYYY-MM-DD)
+ident(m) operator(=) string<delimiter(')content(2007-2-28)delimiter(')> operator(=~) regexp<delimiter(/)content(()char(\\d)content({4}\)-()char(\\d)char(\\d)content(?\)-()char(\\d)char(\\d)content(?\))delimiter(/)>
+keyword(assert) ident(m)operator(.)ident(matches)operator(()(\))
+keyword(assert) operator([)string<delimiter(')content(2007)delimiter(')>operator(,) string<delimiter(')content(2)delimiter(')>operator(,) string<delimiter(')content(28)delimiter(')>(]) operator(==) operator([)ident(m)operator([)integer(0)(])operator([)integer(1)(])operator(,) ident(m)operator([)integer(0)(])operator([)integer(2)(])operator(,) ident(m)operator([)integer(0)(])operator([)integer(3)(]])
+
+ident(usPhoneRegex) operator(=) regexp<delimiter(/)content(^[01]?[- .]?()content(\\()content([2-9])char(\\d)content({2})content(\\\))content(|[2-9])char(\\d)content({2}\)[- .]?)char(\\d)content({3}[- .]?)char(\\d)content({4})content($)delimiter(/)>
+ident(numbers) operator(=) string<delimiter(''')content(
+(425\) 555-0123
+425-555-0123
+425 555 0123
+1-425-555-0123
+)delimiter(''')>operator(.)ident(trim)operator(()(\))operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(toList)operator(()(\))
+keyword(assert) ident(numbers)operator(.)ident(every)operator({) local_variable(it) operator(==~) ident(usPhoneRegex) (})
+
+ident(exclaimRegex) operator(=) regexp<delimiter(/)content((?i\))char(\\b)content(oh)char(\\s)content(+my)char(\\s)content(+gh?o(d(dess(es\)?|s?\)|odness|sh\))char(\\b)delimiter(/)>
+keyword(assert) string<delimiter(')content(Oh my Goodness!)delimiter(')> operator(=~) ident(exclaimRegex)
+keyword(assert) operator(!)operator(()string<delimiter(')content(Golly gosh)delimiter(')> operator(=~) ident(exclaimRegex)(\))
+
+ident(input) operator(=) string<delimiter(')content(line 1)content(\\r)content(line 2)content(\\n)content(line)content(\\r)content(\\n)content(line 3)content(\\n)content(\\r)content(line 4)delimiter(')>
+ident(m) operator(=) ident(input) operator(=~) regexp<delimiter(/)content((?m\)^([^)char(\\012)char(\\015)content(]*\)()char(\\012)char(\\015)content(?|)char(\\015)char(\\012)content(?\))delimiter(/)>
+keyword(assert) ident(m)operator(.)ident(count) operator(==) integer(4)
+
+
+comment(// @@PLEAC@@_6.22)
+comment(// not an exact equivalent to original cookbook but has)
+comment(// a reasonable subset of mostly similar functionality)
+comment(// instead of -r recursion option, use Ant fileset wildcards)
+comment(// e.g. **/*.c. You can also specify an excludes pattern)
+comment(// e.g. **/*.* -X **/*.h will process all but header files)
+comment(// (currently not optimised and with minimal error checking\))
+comment(// uses jopt-simple (jopt-simple.sf.net\))
+
+ident(op) operator(=) keyword(new) ident(joptsimple)operator(.)ident(OptionParser)operator(()(\))
+ident(NOCASE) operator(=) string<delimiter(')content(i)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(NOCASE)operator(,) string<delimiter(")content(case insensitive)delimiter(")> (\))
+ident(WITHN) operator(=) string<delimiter(')content(n)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(WITHN)operator(,) string<delimiter(")content(display line/para with line/para number)delimiter(")> (\))
+ident(WITHF) operator(=) string<delimiter(')content(H)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(WITHF)operator(,) string<delimiter(")content(display line/para with filename)delimiter(")> (\))
+ident(NONAME) operator(=) string<delimiter(')content(h)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(NONAME)operator(,) string<delimiter(")content(hide filenames)delimiter(")> (\))
+ident(COUNT) operator(=) string<delimiter(')content(c)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(COUNT)operator(,) string<delimiter(")content(give count of lines/paras matching)delimiter(")> (\))
+ident(TCOUNT) operator(=) string<delimiter(')content(C)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(TCOUNT)operator(,) string<delimiter(")content(give count of total matches (multiple per line/para\))delimiter(")> (\))
+ident(WORD) operator(=) string<delimiter(')content(w)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(WORD)operator(,) string<delimiter(")content(word boundaries only)delimiter(")> (\))
+ident(EXACT) operator(=) string<delimiter(')content(x)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(EXACT)operator(,) string<delimiter(")content(exact matches only)delimiter(")> (\))
+ident(INVERT) operator(=) string<delimiter(')content(v)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(INVERT)operator(,) string<delimiter(")content(invert search sense (lines that DON'T match\))delimiter(")> (\))
+ident(EXCLUDE) operator(=) string<delimiter(')content(X)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(EXCLUDE)operator(,) string<delimiter(")content(exclude files matching pattern [default is '**/*.bak'])delimiter(")> (\))operator(.)
+ ident(withRequiredArg)operator(()(\))operator(.)ident(describedAs)operator(()string<delimiter(')content(path_pattern)delimiter(')>(\))
+ident(MATCH) operator(=) string<delimiter(')content(l)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(MATCH)operator(,) string<delimiter(")content(list names of files with matches)delimiter(")> (\))
+ident(NOMATCH) operator(=) string<delimiter(')content(L)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(NOMATCH)operator(,) string<delimiter(")content(list names of files with no match)delimiter(")> (\))
+ident(PARA) operator(=) string<delimiter(')content(p)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(PARA)operator(,) string<delimiter(")content(para mode (.* matches newlines\))delimiter(")> (\))operator(.)
+ ident(withOptionalArg)operator(()(\))operator(.)ident(describedAs)operator(()string<delimiter(')content(para_pattern)delimiter(')>(\))
+ident(EXPR) operator(=) string<delimiter(')content(e)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(EXPR)operator(,) string<delimiter(")content(expression (when pattern begins with '-'\))delimiter(")> (\))operator(.)
+ ident(withRequiredArg)operator(()(\))operator(.)ident(describedAs)operator(()string<delimiter(')content(pattern)delimiter(')>(\))
+ident(FILE) operator(=) string<delimiter(')content(f)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(FILE)operator(,) string<delimiter(")content(file containing pattern)delimiter(")> (\))operator(.)
+ ident(withRequiredArg)operator(()(\))operator(.)ident(describedAs)operator(()string<delimiter(')content(filename)delimiter(')>(\))
+ident(HELP) operator(=) string<delimiter(')content(help)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(HELP)operator(,) string<delimiter(")content(display this message)delimiter(")> (\))
+
+ident(options) operator(=) ident(op)operator(.)ident(parse)operator(()ident(args)(\))
+ident(params) operator(=) ident(options)operator(.)ident(nonOptionArguments)operator(()(\))
+keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(() ident(HELP) (\)\)) operator({)
+ ident(op)operator(.)ident(printHelpOn)operator(() pre_type(System)operator(.)ident(out) (\))
+(}) keyword(else) keyword(if) operator(()ident(params)operator(.)ident(size)operator(()(\)) operator(==) integer(0)(\)) operator({)
+ ident(println) string<delimiter(")content(Usage: grep [OPTION]... PATTERN [FILE]...)char(\\n)content(Try 'grep --)inline<inline_delimiter($)ident(HELP)>content(' for more information.)delimiter(")>
+(}) keyword(else) operator({)
+ ident(modifiers) operator(=) type([])
+ ident(paraPattern) operator(=) string<delimiter(')delimiter(')>
+ ident(o_withn) operator(=) ident(options)operator(.)ident(wasDetected)operator(() ident(WITHN) (\))
+ ident(o_withf) operator(=) ident(options)operator(.)ident(wasDetected)operator(() ident(WITHF) (\))
+ ident(o_noname) operator(=) ident(options)operator(.)ident(wasDetected)operator(() ident(NONAME) (\))
+ ident(o_count) operator(=) ident(options)operator(.)ident(wasDetected)operator(() ident(COUNT) (\))
+ ident(o_tcount) operator(=) ident(options)operator(.)ident(wasDetected)operator(() ident(TCOUNT) (\))
+ ident(o_invert) operator(=) ident(options)operator(.)ident(wasDetected)operator(() ident(INVERT) (\))
+ ident(o_match) operator(=) ident(options)operator(.)ident(wasDetected)operator(() ident(MATCH) (\))
+ ident(o_nomatch) operator(=) ident(options)operator(.)ident(wasDetected)operator(() ident(NOMATCH) (\))
+ keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(() ident(EXPR) (\)\)) operator({)
+ ident(pattern) operator(=) ident(options)operator(.)ident(valueOf)operator(() ident(EXPR) (\))
+ (}) keyword(else) keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(() ident(FILE) (\)\)) operator({)
+ ident(pattern) operator(=) keyword(new) pre_type(File)operator(()ident(options)operator(.)ident(valueOf)operator(() ident(FILE) (\)\))operator(.)ident(text)operator(.)ident(trim)operator(()(\))
+ (}) keyword(else) operator({)
+ ident(pattern) operator(=) ident(params)operator([)integer(0)(])
+ ident(params) operator(=) ident(params)operator([)integer(1)operator(..)operator(-)integer(1)(])
+ (})
+ keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(() ident(EXCLUDE) (\)\)) ident(excludes) operator(=) ident(options)operator(.)ident(valueOf)operator(() ident(EXCLUDE) (\))
+ keyword(else) ident(excludes) operator(=) operator([)string<delimiter(')content(**/*.bak)delimiter(')>(])
+ keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(() ident(EXACT) (\)\)) ident(pattern) operator(=) string<delimiter(')content(^)delimiter(')> operator(+) ident(pattern) operator(+) string<delimiter(')content($)delimiter(')>
+ keyword(else) keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(() ident(WORD) (\)\)) ident(pattern) operator(=) regexp<delimiter(/)char(\\b)inline<inline_delimiter($)ident(pattern)>char(\\b)delimiter(/)>
+ keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(() ident(NOCASE) (\)\)) ident(modifiers) operator(+=) string<delimiter(')content(i)delimiter(')>
+ keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(() ident(PARA) (\)\)) operator({)
+ keyword(if) operator(()ident(options)operator(.)ident(hasArgument)operator(() ident(PARA) (\)\)) ident(paraPattern) operator(=) ident(options)operator(.)ident(valueOf)operator(() ident(PARA) (\))
+ keyword(else) ident(paraPattern) operator(=) string<delimiter(')content(^)content($)delimiter(')>
+ ident(paraPattern) operator(=) string<delimiter(')content((?sm\))delimiter(')> operator(+) ident(paraPattern)
+ ident(modifiers) operator(+=) string<delimiter(')content(sm)delimiter(')>
+ (})
+ keyword(if) operator(()ident(modifiers)(\)) ident(pattern) operator(=) string<delimiter(")content((?)inline<inline_delimiter(${)ident(modifiers.join(\))inline_delimiter(})>content(\))delimiter(")> operator(+) ident(pattern)
+
+ keyword(if) operator(()ident(params)operator(.)ident(size)operator(()(\)) operator(==) integer(0)(\)) ident(grepStream)operator(()pre_type(System)operator(.)ident(in)operator(,) string<delimiter(')content(<stdin>)delimiter(')>(\))
+ keyword(else) operator({)
+ ident(scanner) operator(=) keyword(new) ident(AntBuilder)operator(()(\))operator(.)ident(fileScanner) operator({)
+ ident(fileset)operator(()key(dir)operator(:)string<delimiter(')content(.)delimiter(')>operator(,) key(includes)operator(:)ident(params)operator(.)ident(join)operator(()string<delimiter(')content(,)delimiter(')>(\))operator(,) key(excludes)operator(:)ident(excludes)(\))
+ (})
+ keyword(for) operator(()ident(f) keyword(in) ident(scanner)(\)) operator({)
+ ident(grepStream)operator(()keyword(new) pre_type(FileInputStream)operator(()ident(f)(\))operator(,) ident(f)(\))
+ (})
+ (})
+(})
+
+keyword(def) method(grepStream)operator(()ident(s)operator(,) ident(name)(\)) operator({)
+ keyword(def) ident(count) operator(=) integer(0)
+ keyword(def) ident(tcount) operator(=) integer(0)
+ keyword(def) ident(pieces)
+ keyword(if) operator(()ident(paraPattern)(\)) ident(pieces) operator(=) ident(s)operator(.)ident(text)operator(.)ident(split)operator(()ident(paraPattern)(\))
+ keyword(else) ident(pieces) operator(=) ident(s)operator(.)ident(readLines)operator(()(\))
+ keyword(def) ident(fileMode) operator(=) ident(o_match) operator(||) ident(o_nomatch) operator(||) ident(o_count) operator(||) ident(o_tcount)
+ ident(pieces)operator(.)ident(eachWithIndex)operator({)ident(line)operator(,) ident(index) operator(->)
+ keyword(def) ident(m) operator(=) ident(line) operator(=~) ident(pattern)
+ type(boolean) ident(found) operator(=) ident(m)operator(.)ident(count)
+ keyword(if) operator(()ident(found) operator(!=) ident(o_invert)(\)) operator({)
+ ident(count)operator(++)
+ ident(tcount) operator(+=) ident(m)operator(.)ident(count)
+ keyword(if) operator(()operator(!)ident(fileMode)(\)) operator({)
+ ident(linefields) operator(=) type([])
+ keyword(if) operator(()ident(o_withf)(\)) ident(linefields) operator(+=) ident(name)
+ keyword(if) operator(()ident(o_withn)(\)) ident(linefields) operator(+=) ident(index) operator(+) integer(1)
+ ident(linefields) operator(+=) ident(line)
+ ident(println) ident(linefields)operator(.)ident(join)operator(()string<delimiter(')content(:)delimiter(')>(\))
+ (})
+ (})
+ (})
+ keyword(def) ident(display) operator(=) keyword(true)
+ keyword(if) operator(()operator(()ident(o_match) operator(&&) ident(count) operator(==) integer(0)(\)) operator(||) operator(()ident(o_nomatch) operator(&&) ident(count) operator(!=) integer(0)(\)\)) ident(display) operator(=) keyword(false)
+ keyword(if) operator(()ident(fileMode) operator(&&) ident(display)(\)) operator({)
+ ident(filefields) operator(=) type([])
+ keyword(if) operator(()operator(!)ident(o_noname)(\)) ident(filefields) operator(+=) ident(name)
+ keyword(if) operator(()ident(o_tcount)(\)) ident(filefields) operator(+=) ident(tcount)
+ keyword(else) keyword(if) operator(()ident(o_count)(\)) ident(filefields) operator(+=) ident(count)
+ ident(println) ident(filefields)operator(.)ident(join)operator(()string<delimiter(')content(:)delimiter(')>(\))
+ (})
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_7.0)
+comment(//----------------------------------------------------------------------------------)
+comment(//testfile = new File('/usr/local/widgets/data'\) // unix)
+ident(testfile) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/data/blue.txt)delimiter(')>(\)) comment(// windows)
+ident(testfile)operator(.)ident(eachLine)operator({) keyword(if) operator(()local_variable(it) operator(=~) regexp<delimiter(/)content(blue)delimiter(/)>(\)) ident(println) local_variable(it) (})
+
+comment(// Groovy (like Java\) uses the File class as an abstraction for)
+comment(// the path representing a potential file system resource.)
+comment(// Channels and Streams (along with Reader adn Writer helper)
+comment(// classes\) are used to read and write to files (and other)
+comment(// things\). Files, channels, streams etc are all "normal")
+comment(// objects; they can be passed around in your programs just)
+comment(// like other objects (though there are some restrictions)
+comment(// covered elsewhere - e.g. you can't expect to pass a File)
+comment(// object between JVMs on different machines running different)
+comment(// operating systems and expect them to maintain a meaningful)
+comment(// value across the different JVMs\). In addition to Streams,)
+comment(// there is also support for random access to files.)
+
+comment(// Many operations are available on streams and channels. Some)
+comment(// return values to indicate success or failure, some can throw)
+comment(// exceptions, other times both styles of error reporting may be)
+comment(// available.)
+
+comment(// Streams at the lowest level are just a sequence of bytes though)
+comment(// there are various abstractions at higher levels to allow)
+comment(// interacting with streams at encoded character, data type or)
+comment(// object levels if desired. Standard streams include System.in,)
+comment(// System.out and System.err. Java and Groovy on top of that)
+comment(// provide facilities for buffering, filtering and processing)
+comment(// streams in various ways.)
+
+comment(// File channels provide more powerful operations than streams)
+comment(// for reading and writing files such as locks, buffering,)
+comment(// positioning, concurrent reading and writing, mapping to memory)
+comment(// etc. In the examples which follow, streams will be used for)
+comment(// simple cases, channels when more advanced features are)
+comment(// required. Groovy currently focusses on providing extra support)
+comment(// at the file and stream level rather than channel level.)
+comment(// This makes the simple things easy but lets you do more complex)
+comment(// things by just using the appropriate Java classes. All Java)
+comment(// classes are available within Groovy by default.)
+
+comment(// Groovy provides syntactic sugar over the top of Java's file)
+comment(// processing capabilities by providing meaning to shorthand)
+comment(// operators and by automatically handling scaffolding type)
+comment(// code such as opening, closing and handling exceptions behind)
+comment(// the scenes. It also provides many powerful closure operators,)
+comment(// e.g. file.eachLineMatch(pattern\){ some_operation } will open)
+comment(// the file, process it line-by-line, finding all lines which)
+comment(// match the specified pattern and then invoke some operation)
+comment(// for the matching line(s\) if any, before closing the file.)
+
+
+comment(// this example shows how to access the standard input stream)
+comment(// numericCheckingScript:)
+ident(prompt) operator(=) string<delimiter(')content(\\n)content(> )delimiter(')>
+ident(print) string<delimiter(')content(Enter text including a digit:)delimiter(')> operator(+) ident(prompt)
+keyword(new) pre_type(BufferedReader)operator(()keyword(new) pre_type(InputStreamReader)operator(()pre_type(System)operator(.)ident(in)(\)\))operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ comment(// line is read from System.in)
+ keyword(if) operator(()ident(line) operator(=~) string<delimiter(')char(\\\\)content(d)delimiter(')>(\)) ident(println) string<delimiter(")content(Read: )inline<inline_delimiter($)ident(line)>delimiter(")> comment(// normal output to System.out)
+ keyword(else) pre_type(System)operator(.)ident(err)operator(.)ident(println) string<delimiter(')content(No digit found.)delimiter(')> comment(// this message to System.err)
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_7.1)
+comment(//----------------------------------------------------------------------------------)
+comment(// test values (change for your os and directories\))
+ident(inputPath)operator(=)string<delimiter(')content(Pleac/src/pleac7.groovy)delimiter(')>operator(;) ident(outPath)operator(=)string<delimiter(')content(Pleac/temp/junk.txt)delimiter(')>
+
+comment(// For input Java uses InputStreams (for byte-oriented processing\) or Readers)
+comment(// (for character-oriented processing\). These can throw FileNotFoundException.)
+comment(// There are also other stream variants: buffered, data, filters, objects, ...)
+ident(inputFile) operator(=) keyword(new) pre_type(File)operator(()ident(inputPath)(\))
+ident(inputStream) operator(=) keyword(new) pre_type(FileInputStream)operator(()ident(inputFile)(\))
+ident(reader) operator(=) keyword(new) pre_type(FileReader)operator(()ident(inputFile)(\))
+ident(inputChannel) operator(=) ident(inputStream)operator(.)ident(channel)
+
+comment(// Examples for random access to a file)
+ident(file) operator(=) keyword(new) pre_type(RandomAccessFile)operator(()ident(inputFile)operator(,) string<delimiter(")content(rw)delimiter(")>(\)) comment(// for read and write)
+ident(channel) operator(=) ident(file)operator(.)ident(channel)
+
+comment(// Groovy provides some sugar coating on top of Java)
+ident(println) ident(inputFile)operator(.)ident(text)operator(.)ident(size)operator(()(\))
+comment(// => 13496)
+
+comment(// For output Java use OutputStreams or Writers. Can throw FileNotFound)
+comment(// or IO exceptions. There are also other flavours of stream: buffered,)
+comment(// data, filters, objects, ...)
+ident(outFile) operator(=) keyword(new) pre_type(File)operator(()ident(outPath)(\))
+ident(appendFlag) operator(=) keyword(false)
+ident(outStream) operator(=) keyword(new) pre_type(FileOutputStream)operator(()ident(outFile)operator(,) ident(appendFlag)(\))
+ident(writer) operator(=) keyword(new) pre_type(FileWriter)operator(()ident(outFile)operator(,) ident(appendFlag)(\))
+ident(outChannel) operator(=) ident(outStream)operator(.)ident(channel)
+
+comment(// Also some Groovy sugar coating)
+ident(outFile) operator(<)operator(<) string<delimiter(')content(A Chinese sailing vessel)delimiter(')>
+ident(println) ident(outFile)operator(.)ident(text)operator(.)ident(size)operator(()(\)) comment(// => 24)
+
+comment(// @@PLEAC@@_7.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// No problem with Groovy since the filename doesn't contain characters with)
+comment(// special meaning; like Perl's sysopen. Options are either additional parameters)
+comment(// or captured in different classes, e.g. Input vs Output, Buffered vs non etc.)
+keyword(new) pre_type(FileReader)operator(()ident(inputPath)(\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_7.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// '~' is a shell expansion feature rather than file system feature per se.)
+comment(// Because '~' is a valid filename character in some operating systems, and Java)
+comment(// attempts to be cross-platform, it doesn't automatically expand Tilde's.)
+comment(// Given that '~' expansion is commonly used however, Java puts the $HOME)
+comment(// environment variable (used by shells to do typical expansion\) into the)
+comment(// "user.home" system property. This works across operating systems - though)
+comment(// the value inside differs from system to system so you shouldn't rely on its)
+comment(// content to be of a particular format. In most cases though you should be)
+comment(// able to write a regex that will work as expected. Also, Apple's)
+comment(// NSPathUtilities can expand and introduce Tildes on platforms it supports.)
+ident(path) operator(=) string<delimiter(')content(~paulk/.cvspass)delimiter(')>
+ident(name) operator(=) pre_type(System)operator(.)ident(getProperty)operator(()string<delimiter(')content(user.name)delimiter(')>(\))
+ident(home) operator(=) pre_type(System)operator(.)ident(getProperty)operator(()string<delimiter(')content(user.home)delimiter(')>(\))
+ident(println) ident(home) operator(+) ident(path)operator(.)ident(replaceAll)operator(()string<delimiter(")content(~)inline<inline_delimiter($)ident(name)>content((.*\))delimiter(")>operator(,) string<delimiter(')content($)content(1)delimiter(')>(\))
+comment(// => C:\\Documents and Settings\\Paul/.cvspass)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_7.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// The exception raised in Groovy reports the filename)
+keyword(try) operator({)
+ keyword(new) pre_type(File)operator(()string<delimiter(')content(unknown_path/bad_file.ext)delimiter(')>(\))operator(.)ident(text)
+(}) keyword(catch) operator(()pre_type(Exception) ident(ex)(\)) operator({)
+ pre_type(System)operator(.)ident(err)operator(.)ident(println)operator(()ident(ex)operator(.)ident(message)(\))
+(})
+comment(// =>)
+comment(// unknown_path\\bad_file.ext (The system cannot find the path specified\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_7.5)
+comment(//----------------------------------------------------------------------------------)
+keyword(try) operator({)
+ ident(temp) operator(=) pre_type(File)operator(.)ident(createTempFile)operator(()string<delimiter(")content(prefix)delimiter(")>operator(,) string<delimiter(")content(.suffix)delimiter(")>(\))
+ ident(temp)operator(.)ident(deleteOnExit)operator(()(\))
+(}) keyword(catch) operator(()pre_type(IOException) ident(ex)(\)) operator({)
+ pre_type(System)operator(.)ident(err)operator(.)ident(println)operator(()string<delimiter(")content(Temp file could not be created)delimiter(")>(\))
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_7.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// no special features are provided, here is a way to do it manually)
+comment(// DO NOT REMOVE THE FOLLOWING STRING DEFINITION.)
+ident(pleac_7_6_embeddedFileInfo) operator(=) string<delimiter(''')content(
+Script size is 13731
+Last script update: Wed Jan 10 19:05:58 EST 2007
+)delimiter(''')>
+ident(ls) operator(=) pre_type(System)operator(.)ident(getProperty)operator(()string<delimiter(')content(line.separator)delimiter(')>(\))
+ident(file) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/src/pleac7.groovy)delimiter(')>(\))
+ident(regex) operator(=) regexp<delimiter(/)content((?ms\)(?<=^pleac_7_6_embeddedFileInfo = '''\)(.*\)(?=^'''\))delimiter(/)>
+keyword(def) method(readEmbeddedInfo)operator(()(\)) operator({)
+ ident(m) operator(=) ident(file)operator(.)ident(text) operator(=~) ident(regex)
+ ident(println) string<delimiter(')content(Found:)content(\\n)delimiter(')> operator(+) ident(m)operator([)integer(0)(])operator([)integer(1)(])
+(})
+keyword(def) method(writeEmbeddedInfo)operator(()(\)) operator({)
+ ident(lastMod) operator(=) keyword(new) pre_type(Date)operator(()ident(file)operator(.)ident(lastModified)operator(()(\)\))
+ ident(newInfo) operator(=) string<delimiter(")inline<inline_delimiter(${)ident(ls)inline_delimiter(})>content(Script size is )inline<inline_delimiter(${)ident(file.size(\))inline_delimiter(})>inline<inline_delimiter(${)ident(ls)inline_delimiter(})>content(Last script update: )inline<inline_delimiter(${)ident(lastMod)inline_delimiter(})>inline<inline_delimiter(${)ident(ls)inline_delimiter(})>delimiter(")>
+ ident(file)operator(.)ident(write)operator(()ident(file)operator(.)ident(text)operator(.)ident(replaceAll)operator(()ident(regex)operator(,) ident(newInfo)(\)\))
+(})
+ident(readEmbeddedInfo)operator(()(\))
+comment(// writeEmbeddedInfo(\) // uncomment to make script update itself)
+comment(// readEmbeddedInfo(\) // uncomment to redisplay the embedded info after the update)
+
+comment(// => (output when above two method call lines are uncommented\))
+comment(// Found:)
+comment(//)
+comment(// Script size is 13550)
+comment(// Last script update: Wed Jan 10 18:56:03 EST 2007)
+comment(//)
+comment(// Found:)
+comment(//)
+comment(// Script size is 13731)
+comment(// Last script update: Wed Jan 10 19:05:58 EST 2007)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_7.7)
+comment(//----------------------------------------------------------------------------------)
+comment(// general pattern for reading from System.in is:)
+comment(// System.in.readLines(\).each{ processLine(it\) })
+
+comment(// general pattern for a filter which can either process file args or read from System.in is:)
+comment(// if (args.size(\) != 0\) args.each{)
+comment(// file -> new File(file\).eachLine{ processLine(it\) })
+comment(// } else System.in.readLines(\).each{ processLine(it\) })
+
+comment(// note: the following examples are file-related per se. They show)
+comment(// how to do option processing in scenarios which typically also)
+comment(// involve file arguments. The reader should also consider using a)
+comment(// pre-packaged options parser package (there are several popular)
+comment(// ones\) rather than the hard-coded processing examples shown here.)
+
+ident(chopFirst) operator(=) keyword(false)
+ident(columns) operator(=) integer(0)
+ident(args) operator(=) operator([)string<delimiter(')content(-c)delimiter(')>operator(,) string<delimiter(')content(-30)delimiter(')>operator(,) string<delimiter(')content(somefile)delimiter(')>(])
+
+comment(// demo1: optional c)
+keyword(if) operator(()ident(args)operator([)integer(0)(]) operator(==) string<delimiter(')content(-c)delimiter(')>(\)) operator({)
+ ident(chopFirst) operator(=) keyword(true)
+ ident(args) operator(=) ident(args)operator([)integer(1)operator(..)operator(-)integer(1)(])
+(})
+
+keyword(assert) ident(args) operator(==) operator([)string<delimiter(")content(-30)delimiter(")>operator(,) string<delimiter(")content(somefile)delimiter(")>(])
+keyword(assert) ident(chopFirst)
+
+comment(// demo2: processing numerical options)
+keyword(if) operator(()ident(args)operator([)integer(0)(]) operator(=~) regexp<delimiter(/)content(^-()char(\\d)content(+\))content($)delimiter(/)>(\)) operator({)
+ ident(columns) operator(=) ident(args)operator([)integer(0)(])operator([)integer(1)operator(..)operator(-)integer(1)(])operator(.)ident(toInteger)operator(()(\))
+ ident(args) operator(=) ident(args)operator([)integer(1)operator(..)operator(-)integer(1)(])
+(})
+
+keyword(assert) ident(args) operator(==) operator([)string<delimiter(")content(somefile)delimiter(")>(])
+keyword(assert) ident(columns) operator(==) integer(30)
+
+comment(// demo3: multiple args (again consider option parsing package\))
+ident(args) operator(=) operator([)string<delimiter(')content(-n)delimiter(')>operator(,)string<delimiter(')content(-a)delimiter(')>operator(,)string<delimiter(')content(file1)delimiter(')>operator(,)string<delimiter(')content(file2)delimiter(')>(])
+ident(nostdout) operator(=) keyword(false)
+ident(append) operator(=) keyword(false)
+ident(unbuffer) operator(=) keyword(false)
+ident(ignore_ints) operator(=) keyword(false)
+ident(files) operator(=) type([])
+ident(args)operator(.)ident(each)operator({) ident(arg) operator(->)
+ keyword(switch)operator(()ident(arg)(\)) operator({)
+ keyword(case) string<delimiter(')content(-n)delimiter(')>operator(:) ident(nostdout) operator(=) keyword(true)operator(;) keyword(break)
+ keyword(case) string<delimiter(')content(-a)delimiter(')>operator(:) ident(append) operator(=) keyword(true)operator(;) keyword(break)
+ keyword(case) string<delimiter(')content(-u)delimiter(')>operator(:) ident(unbuffer) operator(=) keyword(true)operator(;) keyword(break)
+ keyword(case) string<delimiter(')content(-i)delimiter(')>operator(:) ident(ignore_ints) operator(=) keyword(true)operator(;) keyword(break)
+ keyword(default)operator(:) ident(files) operator(+=) ident(arg)
+ (})
+(})
+keyword(if) operator(()ident(files)operator(.)ident(any)operator({) local_variable(it)operator(.)ident(startsWith)operator(()string<delimiter(')content(-)delimiter(')>(\)}\)) operator({)
+ pre_type(System)operator(.)ident(err)operator(.)ident(println)operator(()string<delimiter(")content(usage: demo3 [-ainu] [filenames])delimiter(")>(\))
+(})
+comment(// process files ...)
+keyword(assert) ident(nostdout) operator(&&) ident(append) operator(&&) operator(!)ident(unbuffer) operator(&&) operator(!)ident(ignore_ints)
+keyword(assert) ident(files) operator(==) operator([)string<delimiter(')content(file1)delimiter(')>operator(,)string<delimiter(')content(file2)delimiter(')>(])
+
+comment(// find login: print all lines containing the string "login" (command-line version\))
+comment(//% groovy -ne "if (line =~ 'login'\) println line" filename)
+
+comment(// find login variation: lines containing "login" with line number (command-line version\))
+comment(//% groovy -ne "if (line =~ 'login'\) println count + ':' + line" filename)
+
+comment(// lowercase file (command-line version\))
+comment(//% groovy -pe "line.toLowerCase(\)")
+
+
+comment(// count chunks but skip comments and stop when reaching "__DATA__" or "__END__")
+ident(chunks) operator(=) integer(0)operator(;) ident(done) operator(=) keyword(false)
+ident(testfile) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/data/chunks.txt)delimiter(')>(\)) comment(// change on your system)
+ident(lines) operator(=) ident(testfile)operator(.)ident(readLines)operator(()(\))
+keyword(for) operator(()ident(line) keyword(in) ident(lines)(\)) operator({)
+ keyword(if) operator(()operator(!)ident(line)operator(.)ident(trim)operator(()(\)\)) keyword(continue)
+ ident(words) operator(=) ident(line)operator(.)ident(split)operator(()regexp<delimiter(/)content([^)char(\\w)content(#]+)delimiter(/)>(\))operator(.)ident(toList)operator(()(\))
+ keyword(for) operator(()ident(word) keyword(in) ident(words)(\)) operator({)
+ keyword(if) operator(()ident(word) operator(=~) regexp<delimiter(/)content(^#)delimiter(/)>(\)) keyword(break)
+ keyword(if) operator(()ident(word) keyword(in) operator([)string<delimiter(")content(__DATA__)delimiter(")>operator(,) string<delimiter(")content(__END__)delimiter(")>(]\)) operator({) ident(done) operator(=) keyword(true)operator(;) keyword(break) (})
+ ident(chunks) operator(+=) integer(1)
+ (})
+ keyword(if) operator(()ident(done)(\)) keyword(break)
+(})
+ident(println) string<delimiter(")content(Found )inline<inline_delimiter($)ident(chunks)>content( chunks)delimiter(")>
+
+
+comment(// groovy "one-liner" (cough cough\) for turning .history file into pretty version:)
+comment(//% groovy -e "m=new File(args[0]\).text=~/(?ms\)^#\\+(\\d+\)\\r?\\n(.*?\)$/;(0..<m.count\).each{println ''+new Date(m[it][1].toInteger(\)\)+' '+m[it][2]}" .history)
+comment(// =>)
+comment(// Sun Jan 11 18:26:22 EST 1970 less /etc/motd)
+comment(// Sun Jan 11 18:26:22 EST 1970 vi ~/.exrc)
+comment(// Sun Jan 11 18:26:22 EST 1970 date)
+comment(// Sun Jan 11 18:26:22 EST 1970 who)
+comment(// Sun Jan 11 18:26:22 EST 1970 telnet home)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_7.8)
+comment(//----------------------------------------------------------------------------------)
+comment(// test data for below)
+ident(testPath) operator(=) string<delimiter(')content(Pleac/data/process.txt)delimiter(')>
+
+comment(// general pattern)
+keyword(def) method(processWithBackup)operator(()ident(inputPath)operator(,) ident(Closure) ident(processLine)(\)) operator({)
+ keyword(def) ident(input) operator(=) keyword(new) pre_type(File)operator(()ident(inputPath)(\))
+ keyword(def) ident(out) operator(=) pre_type(File)operator(.)ident(createTempFile)operator(()string<delimiter(")content(prefix)delimiter(")>operator(,) string<delimiter(")content(.suffix)delimiter(")>(\))
+ ident(out)operator(.)ident(write)operator(()string<delimiter(')delimiter(')>(\)) comment(// create empty file)
+ ident(count) operator(=) integer(0)
+ ident(input)operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ ident(count)operator(++)
+ ident(processLine)operator(()ident(out)operator(,) ident(line)operator(,) ident(count)(\))
+ (})
+ keyword(def) ident(dest) operator(=) keyword(new) pre_type(File)operator(()ident(inputPath) operator(+) string<delimiter(")content(.orig)delimiter(")>(\))
+ ident(dest)operator(.)ident(delete)operator(()(\)) comment(// clobber previous backup)
+ ident(input)operator(.)ident(renameTo)operator(()ident(dest)(\))
+ ident(out)operator(.)ident(renameTo)operator(()ident(input)(\))
+(})
+
+comment(// use withPrintWriter if you don't want the '\\n''s appearing)
+ident(processWithBackup)operator(()ident(testPath)(\)) operator({) ident(out)operator(,) ident(line)operator(,) ident(count) operator(->)
+ keyword(if) operator(()ident(count) operator(==) integer(20)(\)) operator({) comment(// we are at the 20th line)
+ ident(out) operator(<)operator(<) string<delimiter(")content(Extra line 1)char(\\n)delimiter(")>
+ ident(out) operator(<)operator(<) string<delimiter(")content(Extra line 2)char(\\n)delimiter(")>
+ (})
+ ident(out) operator(<)operator(<) ident(line) operator(+) string<delimiter(')content(\\n)delimiter(')>
+(})
+
+ident(processWithBackup)operator(()ident(testPath)(\)) operator({) ident(out)operator(,) ident(line)operator(,) ident(count) operator(->)
+ keyword(if) operator(()operator(!)operator(()ident(count) keyword(in) integer(20)operator(..)integer(30)(\)\)) comment(// skip the 20th line to the 30th)
+ ident(out) operator(<)operator(<) ident(line) operator(+) string<delimiter(')content(\\n)delimiter(')>
+(})
+comment(// equivalent to "one-liner":)
+comment(//% groovy -i.orig -pe "if (!(count in 20..30\)\) out << line" testPath)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_7.9)
+comment(//----------------------------------------------------------------------------------)
+comment(//% groovy -i.orig -pe 'FILTER COMMAND' file1 file2 file3 ...)
+
+comment(// the following may also be possible on unix systems (unchecked\))
+comment(//#!/usr/bin/groovy -i.orig -p)
+comment(// filter commands go here)
+
+comment(// "one-liner" templating scenario: change DATE -> current time)
+comment(//% groovy -pi.orig -e 'line.replaceAll(/DATE/\){new Date(\)}')
+
+comment(//% groovy -i.old -pe 'line.replaceAll(/\\bhisvar\\b/, 'hervar'\)' *.[Cchy] (globbing platform specific\))
+
+comment(// one-liner for correcting spelling typos)
+comment(//% groovy -i.orig -pe 'line.replaceAll(/\\b(p\)earl\\b/i, '\\1erl'\)' *.[Cchy] (globbing platform specific\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_7.10)
+comment(//----------------------------------------------------------------------------------)
+comment(// general pattern)
+keyword(def) method(processFileInplace)operator(()ident(file)operator(,) ident(Closure) ident(processText)(\)) operator({)
+ keyword(def) ident(text) operator(=) ident(file)operator(.)ident(text)
+ ident(file)operator(.)ident(write)operator(()ident(processText)operator(()ident(text)(\)\))
+(})
+
+comment(// templating scenario: change DATE -> current time)
+ident(testfile) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/data/pleac7_10.txt)delimiter(')>(\)) comment(// replace on your system)
+ident(processFileInplace)operator(()ident(testfile)(\)) operator({) ident(text) operator(->)
+ ident(text)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((?m\)DATE)delimiter(/)>operator(,) keyword(new) pre_type(Date)operator(()(\))operator(.)ident(toString)operator(()(\)\))
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_7.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// You need to use Java's Channel class to acquire locks. The exact)
+comment(// nature of the lock is somewhat dependent on the operating system.)
+keyword(def) method(processFileWithLock)operator(()ident(file)operator(,) ident(processStream)(\)) operator({)
+ keyword(def) ident(random) operator(=) keyword(new) pre_type(RandomAccessFile)operator(()ident(file)operator(,) string<delimiter(")content(rw)delimiter(")>(\))
+ keyword(def) ident(lock) operator(=) ident(random)operator(.)ident(channel)operator(.)ident(lock)operator(()(\)) comment(// acquire exclusive lock)
+ ident(processStream)operator(()ident(random)(\))
+ ident(lock)operator(.)ident(release)operator(()(\))
+ ident(random)operator(.)ident(close)operator(()(\))
+(})
+
+comment(// Instead of an exclusive lock you can acquire a shared lock.)
+
+comment(// Also, you can acquire a lock for a region of a file by specifying)
+comment(// start and end positions of the region when acquiring the lock.)
+
+comment(// For non-blocking functionality, use tryLock(\) instead of lock(\).)
+keyword(def) method(processFileWithTryLock)operator(()ident(file)operator(,) ident(processStream)(\)) operator({)
+ ident(random) operator(=) keyword(new) pre_type(RandomAccessFile)operator(()ident(file)operator(,) string<delimiter(")content(rw)delimiter(")>(\))
+ ident(channel) operator(=) ident(random)operator(.)ident(channel)
+ keyword(def) ident(MAX_ATTEMPTS) operator(=) integer(30)
+ keyword(for) operator(()ident(i) keyword(in) integer(0)operator(..<)ident(MAX_ATTEMPTS)(\)) operator({)
+ ident(lock) operator(=) ident(channel)operator(.)ident(tryLock)operator(()(\))
+ keyword(if) operator(()ident(lock) operator(!=) keyword(null)(\)) keyword(break)
+ ident(println) string<delimiter(')content(Could not get lock, pausing ...)delimiter(')>
+ pre_type(Thread)operator(.)ident(sleep)operator(()integer(500)(\)) comment(// 500 millis = 0.5 secs)
+ (})
+ keyword(if) operator(()ident(lock) operator(==) keyword(null)(\)) operator({)
+ ident(println) string<delimiter(')content(Unable to acquire lock, aborting ...)delimiter(')>
+ (}) keyword(else) operator({)
+ ident(processStream)operator(()ident(random)(\))
+ ident(lock)operator(.)ident(release)operator(()(\))
+ (})
+ ident(random)operator(.)ident(close)operator(()(\))
+(})
+
+
+comment(// non-blocking multithreaded example: print first line while holding lock)
+pre_type(Thread)operator(.)ident(start)operator({)
+ ident(processFileWithLock)operator(()ident(testfile)(\)) operator({) ident(source) operator(->)
+ ident(println) string<delimiter(')content(First reader: )delimiter(')> operator(+) ident(source)operator(.)ident(readLine)operator(()(\))operator(.)ident(toUpperCase)operator(()(\))
+ pre_type(Thread)operator(.)ident(sleep)operator(()integer(2000)(\)) comment(// 2000 millis = 2 secs)
+ (})
+(})
+ident(processFileWithTryLock)operator(()ident(testfile)(\)) operator({) ident(source) operator(->)
+ ident(println) string<delimiter(')content(Second reader: )delimiter(')> operator(+) ident(source)operator(.)ident(readLine)operator(()(\))operator(.)ident(toUpperCase)operator(()(\))
+(})
+comment(// =>)
+comment(// Could not get lock, pausing ...)
+comment(// First reader: WAS LOWERCASE)
+comment(// Could not get lock, pausing ...)
+comment(// Could not get lock, pausing ...)
+comment(// Could not get lock, pausing ...)
+comment(// Could not get lock, pausing ...)
+comment(// Second reader: WAS LOWERCASE)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_7.12)
+comment(//----------------------------------------------------------------------------------)
+comment(// In Java, input and output streams have a flush(\) method and file channels)
+comment(// have a force(\) method (applicable also to memory-mapped files\). When creating)
+comment(// PrintWriters and // PrintStreams, an autoFlush option can be provided.)
+comment(// From a FileInput or Output Stream you can ask for the FileDescriptor)
+comment(// which has a sync(\) method - but you wouldn't you'd just use flush(\).)
+
+ident(inputStream) operator(=) ident(testfile)operator(.)ident(newInputStream)operator(()(\)) comment(// returns a buffered input stream)
+ident(autoFlush) operator(=) keyword(true)
+ident(printStream) operator(=) keyword(new) pre_type(PrintStream)operator(()ident(outStream)operator(,) ident(autoFlush)(\))
+ident(printWriter) operator(=) keyword(new) pre_type(PrintWriter)operator(()ident(outStream)operator(,) ident(autoFlush)(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_7.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// See the comments in 7.14 about scenarios where non-blocking can be)
+comment(// avoided. Also see 7.14 regarding basic information about channels.)
+comment(// An advanced feature of the java.nio.channels package is supported)
+comment(// by the Selector and SelectableChannel classes. These allow efficient)
+comment(// server multiplexing amongst responses from a number of potential sources.)
+comment(// Under the covers, it allows mapping to native operating system features)
+comment(// supporting such multiplexing or using a pool of worker processing threads)
+comment(// much smaller in size than the total available connections.)
+comment(//)
+comment(// The general pattern for using selectors is:)
+comment(//)
+comment(// while (true\) {)
+comment(// selector.select(\))
+comment(// def it = selector.selectedKeys(\).iterator(\))
+comment(// while (it.hasNext(\)\) {)
+comment(// handleKey(it++\))
+comment(// it.remove(\))
+comment(// })
+comment(// })
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_7.14)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy has no special support for this apart from making it easier to)
+comment(// create threads (see note at end\); it relies on Java's features here.)
+
+comment(// InputStreams in Java/Groovy block if input is not yet available.)
+comment(// This is not normally an issue, because if you have a potential blocking)
+comment(// operation, e.g. save a large file, you normally just create a thread)
+ comment(// and save it in the background.)
+
+comment(// Channels are one way to do non-blocking stream-based IO.)
+comment(// Classes which implement the AbstractSelectableChannel interface provide)
+comment(// a configureBlocking(boolean\) method as well as an isBlocking(\) method.)
+comment(// When processing a non-blocking stream, you need to process incoming)
+comment(// information based on the number of bytes read returned by the various)
+comment(// read methods. For non-blocking, this can be 0 bytes even if you pass)
+comment(// a fixed size byte[] buffer to the read method. Non-blocking IO is typically)
+comment(// not used with Files but more normally with network streams though they)
+comment(// can when Pipes (couple sink and source channels\) are involved where)
+comment(// one side of the pipe is a file.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_7.15)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy uses Java's features here.)
+comment(// For both blocking and non-blocking reads, the read operation returns the number)
+comment(// of bytes read. In blocking operations, this normally corresponds to the number)
+comment(// of bytes requested (typically the size of some buffer\) but can have a smaller)
+comment(// value at the end of a stream. Java also makes no guarantees about whether)
+comment(// other streams in general will return bytes as they become available under)
+comment(// certain circumstances (rather than blocking until the entire buffer is filled.)
+comment(// In non-blocking operations, the number of bytes returned will typically be)
+comment(// the number of bytes available (up to some maximum buffer or requested size\).)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_7.16)
+comment(//----------------------------------------------------------------------------------)
+comment(// This just works in Java and Groovy as per the previous examples.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_7.17)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy uses Java's features here.)
+comment(// More work has been done in the Java on object caching than file caching)
+comment(// with several open source and commercial offerings in that area. File caches)
+comment(// are also available, for one, see:)
+comment(// http://portals.apache.org/jetspeed-1/apidocs/org/apache/jetspeed/cache/FileCache.html)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_7.18)
+comment(//----------------------------------------------------------------------------------)
+comment(// The general pattern is: streams.each{ stream -> stream.println 'item to print' })
+comment(// See the MultiStream example in 13.5 for a coded example.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_7.19)
+comment(//----------------------------------------------------------------------------------)
+comment(// You wouldn't normally be dealing with FileDescriptors. In case were you have)
+comment(// one you would normally walk through all known FileStreams asking each for)
+comment(// it's FileDescriptor until you found one that matched. You would then close)
+comment(// that stream.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_7.20)
+comment(//----------------------------------------------------------------------------------)
+comment(// There are several concepts here. At the object level, any two object references)
+comment(// can point to the same object. Any changes made by one of these will be visible)
+comment(// in the 'alias'. You can also have multiple stream, reader, writer or channel objects)
+comment(// referencing the same resource. Depending on the kind of resource, any potential)
+comment(// locks, the operations being requested and the behaviour of third-party programs,)
+comment(// the result of trying to perform such concurrent operations may not always be)
+comment(// deterministic. There are strategies for coping with such scenarious but the)
+comment(// best bet is to avoid the issue.)
+
+comment(// For the scenario given, copying file handles, that corresponds most closely)
+comment(// with cloning streams. The best bet is to just use individual stream objects)
+comment(// both created from the same file. If you are attempting to do write operations,)
+comment(// then you should consider using locks.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_7.21)
+comment(//----------------------------------------------------------------------------------)
+comment(// locking is built in to Java (since 1.4\), so should not be missing)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_7.22)
+comment(//----------------------------------------------------------------------------------)
+comment(// Java locking supports locking just regions of files.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.0)
+comment(//----------------------------------------------------------------------------------)
+ident(datafile) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/data/pleac8_0.txt)delimiter(')>(\)) comment(// change on your system)
+
+ident(datafile)operator(.)ident(eachLine)operator({) ident(line) operator(->) ident(print) ident(line)operator(.)ident(size)operator(()(\)) (})
+
+ident(lines) operator(=) ident(datafile)operator(.)ident(readLines)operator(()(\))
+
+ident(wholeTextFile) operator(=) ident(datafile)operator(.)ident(text)
+
+comment(// on command line Groovy use -a auto split pattern instead of record separator)
+comment(// default pattern is /\\s/)
+comment(// groovy -a -e 'println "First word is ${split[0][1]}"')
+
+comment(// (additional examples to original cookbook to illustrate -a\))
+comment(// Print processes owned by root:)
+comment(// ps aux|groovy -ane "if(split[0][1] =~ 'root'\)println split[0][10..-1]")
+
+comment(// Print all logins from /etc/passwd that are not commented:)
+comment(// groovy -a':' -ne "if(!(split[0][1] =~ /^#/\)\)println split[0][1]" /etc/passwd)
+
+comment(// Add the first and the penultimate column of a file:)
+comment(// groovy -ape "split[0][1].toInteger(\)+split[0][-2].toInteger(\)" accounts.txt)
+
+comment(// no BEGIN and END in Groovy (has been proposed, may be added soon\))
+
+ident(datafile)operator(.)ident(withOutputStream)operator({) ident(stream) operator(->)
+ ident(stream)operator(.)ident(print) string<delimiter(")content(one)delimiter(")> operator(+) string<delimiter(")content(two)delimiter(")> operator(+) string<delimiter(")content(three)delimiter(")> comment(// "onetwothree" -> file)
+ ident(println) string<delimiter(")content(Baa baa black sheep.)delimiter(")> comment(// sent to $stdout)
+(})
+
+comment(// use streams or channels for advanced file handling)
+type(int) ident(size) operator(=) ident(datafile)operator(.)ident(size)operator(()(\))
+ident(buffer) operator(=) pre_type(ByteBuffer)operator(.)ident(allocate)operator(()ident(size)(\)) comment(// for large files, use some block size, e.g. 4096)
+ident(channel) operator(=) keyword(new) pre_type(FileInputStream)operator(()ident(datafile)(\))operator(.)ident(channel)
+ident(println) string<delimiter(")content(Number of bytes read was: )inline<inline_delimiter(${)ident(channel.read(buffer\))inline_delimiter(})>delimiter(")> comment(// -1 = EOF)
+
+ident(channel) operator(=) keyword(new) pre_type(FileOutputStream)operator(()pre_type(File)operator(.)ident(createTempFile)operator(()string<delimiter(")content(pleac8)delimiter(")>operator(,) string<delimiter(")content(.junk)delimiter(")>(\)\))operator(.)ident(channel)
+ident(size) operator(=) ident(channel)operator(.)ident(size)operator(()(\))
+ident(channel)operator(.)ident(truncate)operator(()ident(size)(\)) comment(// shrinks file (in our case to same size\))
+
+ident(pos) operator(=) ident(channel)operator(.)ident(position)operator(()(\))
+ident(println) string<delimiter(")content(I'm )inline<inline_delimiter($)ident(pos)>content( bytes from the start of datafile)delimiter(")>
+ident(channel)operator(.)ident(position)operator(()ident(pos)(\)) comment(// move to pos (in our case unchanged\))
+ident(channel)operator(.)ident(position)operator(()integer(0)(\)) comment(// move to start of file)
+ident(channel)operator(.)ident(position)operator(()ident(size)(\)) comment(// move to end of file)
+
+comment(// no sysread and syswrite are available but dataInput/output streams)
+comment(// can be used to achieve similar functionality, see 8.15.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.1)
+comment(//----------------------------------------------------------------------------------)
+ident(testfile) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/data/pleac8_1.txt)delimiter(')>(\)) comment(// change on your system)
+comment(// contents of testfile:)
+comment(// DISTFILES = $(DIST_COMMON\) $(SOURCES\) $(HEADERS\) \\
+// $(TEXINFOS\) $(INFOS\) $(MANS\) $(DATA\))
+comment(// DEP_DISTFILES = $(DIST_COMMON\) $(SOURCES\) $(HEADERS\) \\
+// $(TEXINFOS\) $(INFO_DEPS\) $(MANS\) $(DATA\) \\
+// $(EXTRA_DIST\))
+
+ident(lines) operator(=) type([])
+ident(continuing) operator(=) keyword(false)
+ident(regex) operator(=) regexp<delimiter(/)char(\\\\)content($)delimiter(/)>
+ident(testfile)operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ ident(stripped) operator(=) ident(line)operator(.)ident(replaceAll)operator(()ident(regex)operator(,)string<delimiter(')delimiter(')>(\))
+ keyword(if) operator(()ident(continuing)(\)) ident(lines)operator([)operator(-)integer(1)(]) operator(+=) ident(stripped)
+ keyword(else) ident(lines) operator(+=) ident(stripped)
+ ident(continuing) operator(=) operator(()ident(line) operator(=~) ident(regex)(\))
+(})
+ident(println) ident(lines)operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+comment(// =>)
+comment(// DISTFILES = $(DIST_COMMON\) $(SOURCES\) $(HEADERS\) $(TEXINFOS\) $(INFOS\) $(MANS\) $(DATA\))
+comment(// DEP_DISTFILES = $(DIST_COMMON\) $(SOURCES\) $(HEADERS\) $(TEXINFOS\) $(INFO_DEPS\) $(MANS\) $(DATA\) $(EXTRA_DIST\))
+
+comment(// to remove hidden spaces after the slash (but keep the slash\):)
+keyword(def) method(trimtail)operator(()ident(line)(\)) operator({)
+ ident(line) operator(=) ident(line)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((?<=)char(\\\\)content(\))char(\\s)content(*)content($)delimiter(/)>operator(,) string<delimiter(')delimiter(')>(\))
+(})
+ident(b) operator(=) regexp<delimiter(/)char(\\\\)delimiter(/)> comment(// backslash)
+keyword(assert) string<delimiter(")content(abc )inline<inline_delimiter($)ident(b)>delimiter(")> operator(==) ident(trimtail)operator(()string<delimiter(")content(abc )inline<inline_delimiter($)ident(b)>delimiter(")>(\))
+keyword(assert) string<delimiter(")content(abc )delimiter(")> operator(==) ident(trimtail)operator(()string<delimiter(")content(abc )delimiter(")>(\))
+keyword(assert) string<delimiter(")content(abc )inline<inline_delimiter($)ident(b)>delimiter(")> operator(==) ident(trimtail)operator(()string<delimiter(")content(abc )inline<inline_delimiter($)ident(b)>content( )delimiter(")>(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// unixScript:)
+ident(println) operator(()string<delimiter(")content(wc -l < )inline<inline_delimiter($)ident(filename)>delimiter(")>operator(.)ident(execute)operator(()(\))operator(.)ident(text)(\))
+
+comment(// for small files which fit in memory)
+ident(println) ident(testfile)operator(.)ident(readLines)operator(()(\))operator(.)ident(size)operator(()(\))
+
+comment(// streaming approach (lines and paras\))
+ident(lines) operator(=) integer(0)operator(;) ident(paras) operator(=) integer(1)
+ident(testfile)operator(.)ident(eachLine)operator({) ident(lines)operator(++)operator(;) keyword(if) operator(()local_variable(it) operator(=~) regexp<delimiter(/)content(^)content($)delimiter(/)>(\)) ident(paras)operator(++) (})
+ident(println) string<delimiter(")content(Found )inline<inline_delimiter($)ident(lines)>content( lines and )inline<inline_delimiter($)ident(paras)>content( paras.)delimiter(")>
+comment(// note: counts blank line at end as start of next empty para)
+
+comment(// with a StreamTokenizer)
+ident(st) operator(=) keyword(new) pre_type(StreamTokenizer)operator(()ident(testfile)operator(.)ident(newReader)operator(()(\)\))
+keyword(while) operator(()ident(st)operator(.)ident(nextToken)operator(()(\)) operator(!=) pre_type(StreamTokenizer)operator(.)ident(TT_EOF)(\)) operator({)(})
+ident(println) ident(st)operator(.)ident(lineno)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// general pattern)
+keyword(def) method(processWordsInFile)operator(()ident(file)operator(,) ident(processWord)(\)) operator({)
+ ident(testfile)operator(.)ident(splitEachLine)operator(()regexp<delimiter(/)char(\\W)content(+)delimiter(/)>(\)) operator({) ident(matched) operator(->)
+ ident(matched)operator(.)ident(each)operator({) ident(w) operator(->) keyword(if) operator(()ident(w)(\)) ident(processWord)operator(()ident(w)(\)) (})
+ (})
+(})
+
+ident(testfile) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/src/pleac8.groovy)delimiter(')>(\)) comment(// change path on your system)
+
+comment(// count words)
+ident(count) operator(=) integer(0)
+ident(processWordsInFile)operator(()ident(testfile)(\))operator({) ident(count)operator(++) (})
+ident(println) ident(count)
+
+comment(// (variation to Perl example\))
+comment(// with a StreamTokenizer (counting words and numbers in Pleac chapter 8 source file\))
+ident(words) operator(=) integer(0)operator(;) ident(numbers) operator(=) integer(0)
+ident(st) operator(=) keyword(new) pre_type(StreamTokenizer)operator(()ident(testfile)operator(.)ident(newReader)operator(()(\)\))
+ident(st)operator(.)ident(slashSlashComments)operator(()keyword(true)(\)) comment(// ignore words and numbers in comments)
+keyword(while) operator(()ident(st)operator(.)ident(nextToken)operator(()(\)) operator(!=) pre_type(StreamTokenizer)operator(.)ident(TT_EOF)(\)) operator({)
+ keyword(if) operator(()ident(st)operator(.)ident(ttype) operator(==) pre_type(StreamTokenizer)operator(.)ident(TT_WORD)(\)) ident(words)operator(++)
+ keyword(else) keyword(if) operator(()ident(st)operator(.)ident(ttype) operator(==) pre_type(StreamTokenizer)operator(.)ident(TT_NUMBER)(\)) ident(numbers)operator(++)
+(})
+ident(println) string<delimiter(")content(Found )inline<inline_delimiter($)ident(words)>content( words and )inline<inline_delimiter($)ident(numbers)>content( numbers.)delimiter(")>
+
+
+comment(// word frequency count)
+ident(seen) operator(=) operator([)operator(:)(])
+ident(processWordsInFile)operator(()ident(testfile)(\)) operator({)
+ ident(w) operator(=) local_variable(it)operator(.)ident(toLowerCase)operator(()(\))
+ keyword(if) operator(()ident(seen)operator(.)ident(containsKey)operator(()ident(w)(\)\)) ident(seen)operator([)ident(w)(]) operator(+=) integer(1)
+ keyword(else) ident(seen)operator([)ident(w)(]) operator(=) integer(1)
+(})
+comment(// output map in a descending numeric sort of its values)
+ident(seen)operator(.)ident(entrySet)operator(()(\))operator(.)ident(sort) operator({) ident(a)operator(,)ident(b) operator(->) ident(b)operator(.)ident(value) operator(<=)operator(>) ident(a)operator(.)ident(value) (})operator(.)ident(each)operator({) ident(e) operator(->)
+ ident(printf)operator(()string<delimiter(")content(%5d %s)char(\\n)delimiter(")>operator(,) operator([)ident(e)operator(.)ident(value)operator(,) ident(e)operator(.)ident(key)(]) (\))
+(})
+comment(// =>)
+comment(// 25 pleac)
+comment(// 22 line)
+comment(// 20 file)
+comment(// 19 println)
+comment(// 19 lines)
+comment(// 13 testfile)
+comment(// ...)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.4)
+comment(//----------------------------------------------------------------------------------)
+ident(testfile)operator(.)ident(readLines)operator(()(\))operator(.)ident(reverseEach)operator({)
+ ident(println) local_variable(it)
+(})
+
+ident(lines) operator(=) ident(testfile)operator(.)ident(readLines)operator(()(\))
+comment(// normally one would use the reverseEach, but you can use)
+comment(// a numerical index if you want)
+operator(()operator(()ident(lines)operator(.)ident(size)operator(()(\)) operator(-) integer(1)(\))operator(..)integer(0)(\))operator(.)ident(each)operator({)
+ ident(println) ident(lines)operator([)local_variable(it)(])
+(})
+
+comment(// Paragraph-based processing could be done as in 8.2.)
+
+comment(// A streaming-based solution could use random file access)
+comment(// and have a sliding buffer working from the back of the)
+comment(// file to the front.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.5)
+comment(//----------------------------------------------------------------------------------)
+ident(logfile) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/data/sampleLog.txt)delimiter(')>(\))
+comment(// logTailingScript:)
+ident(sampleInterval) operator(=) integer(2000) comment(// 2000 millis = 2 secs)
+ident(file) operator(=) keyword(new) pre_type(RandomAccessFile)operator(() ident(logfile)operator(,) string<delimiter(")content(r)delimiter(")> (\))
+ident(filePointer) operator(=) integer(0) comment(// set to logfile.size(\) to begin tailing from the end of the file)
+keyword(while)operator(() keyword(true) (\)) operator({)
+ comment(// Compare the length of the file to the file pointer)
+ type(long) ident(fileLength) operator(=) ident(logfile)operator(.)ident(size)operator(()(\))
+ keyword(if)operator(() ident(fileLength) operator(<) ident(filePointer) (\)) operator({)
+ comment(// Log file must have been rotated or deleted;)
+ pre_type(System)operator(.)ident(err)operator(.)ident(println) string<delimiter(")inline<inline_delimiter(${)ident(new Date(\))inline_delimiter(})>content(: Reopening )inline<inline_delimiter($)ident(logfile)>delimiter(")>
+ ident(file) operator(=) keyword(new) pre_type(RandomAccessFile)operator(() ident(logfile)operator(,) string<delimiter(")content(r)delimiter(")> (\))
+ ident(filePointer) operator(=) integer(0)
+ (})
+ keyword(if)operator(() ident(fileLength) operator(>) ident(filePointer) (\)) operator({)
+ comment(// There is data to read)
+ ident(file)operator(.)ident(seek)operator(() ident(filePointer) (\))
+ keyword(while)operator(() operator(()ident(line) operator(=) ident(file)operator(.)ident(readLine)operator(()(\)\)) operator(!=) keyword(null) (\)) operator({)
+ ident(println) string<delimiter(')content(##)delimiter(')> operator(+) ident(line)
+ (})
+ ident(filePointer) operator(=) ident(file)operator(.)ident(filePointer)
+ (})
+ comment(// Sleep for the specified interval)
+ pre_type(Thread)operator(.)ident(sleep)operator(() ident(sampleInterval) (\))
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.6)
+comment(//----------------------------------------------------------------------------------)
+comment(//testfile = newFile('/usr/share/fortune/humorists'\))
+
+comment(// small files:)
+ident(random) operator(=) keyword(new) pre_type(Random)operator(()(\))
+ident(lines) operator(=) ident(testfile)operator(.)ident(readLines)operator(()(\))
+ident(println) ident(lines)operator([)ident(random)operator(.)ident(nextInt)operator(()ident(lines)operator(.)ident(size)operator(()(\)\)])
+
+comment(// streamed alternative)
+ident(count) operator(=) integer(0)
+keyword(def) ident(adage)
+ident(testfile)operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ ident(count)operator(++)
+ keyword(if) operator(()ident(random)operator(.)ident(nextInt)operator(()ident(count)(\)) operator(<) integer(1)(\)) ident(adage) operator(=) ident(line)
+(})
+ident(println) ident(adage)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.7)
+comment(//----------------------------------------------------------------------------------)
+comment(// non-streamed solution (like Perl and Ruby\))
+ident(lines) operator(=) ident(testfile)operator(.)ident(readLines)operator(()(\))
+pre_type(Collections)operator(.)ident(shuffle)operator(()ident(lines)(\))
+ident(println) ident(lines)operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.8)
+comment(//----------------------------------------------------------------------------------)
+ident(desiredLine) operator(=) integer(235)
+comment(// for small files)
+ident(lines) operator(=) ident(testfile)operator(.)ident(readLines)operator(()(\))
+ident(println) string<delimiter(")content(Line )inline<inline_delimiter($)ident(desiredLine)>content(: )inline<inline_delimiter(${)ident(lines[desiredLine-1])inline_delimiter(})>delimiter(")>
+
+comment(// streaming solution)
+ident(reader) operator(=) ident(testfile)operator(.)ident(newReader)operator(()(\))
+ident(count) operator(=) integer(0)
+keyword(def) ident(line)
+keyword(while) operator(()operator(()ident(line) operator(=) ident(reader)operator(.)ident(readLine)operator(()(\)\))operator(!=) keyword(null)(\)) operator({)
+ keyword(if) operator(()operator(++)ident(count) operator(==) ident(desiredLine)(\)) keyword(break)
+(})
+ident(println) string<delimiter(")content(Line )inline<inline_delimiter($)ident(desiredLine)>content(: )inline<inline_delimiter($)ident(line)>delimiter(")>
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.9)
+comment(//----------------------------------------------------------------------------------)
+ident(println) ident(testfile)operator(.)ident(text)operator(.)ident(split)operator(()regexp<delimiter(/)content(@@pleac@@_8.)delimiter(/)modifier(i)>(\))operator(.)ident(size)operator(()(\))
+comment(// => 23 (21 sections .0 .. .20 plus before .0 plus line above\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.10)
+comment(//----------------------------------------------------------------------------------)
+ident(file) operator(=) keyword(new) pre_type(RandomAccessFile)operator(() ident(logfile)operator(,) string<delimiter(")content(rw)delimiter(")> (\))
+type(long) ident(previous)operator(,) ident(lastpos) operator(=) integer(0)
+keyword(while)operator(() operator(()ident(line) operator(=) ident(file)operator(.)ident(readLine)operator(()(\)\)) operator(!=) keyword(null) (\)) operator({)
+ ident(previous) operator(=) ident(lastpos)
+ ident(lastpos) operator(=) ident(file)operator(.)ident(filePointer)
+(})
+keyword(if) operator(()ident(previous)(\)) ident(file)operator(.)ident(setLength)operator(()ident(previous)(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// Java's streams are binary at the lowest level if not processed with)
+comment(// higher level stream mechanisms or readers/writers. Some additions)
+comment(// to the Perl cookbook which illustrate the basics.)
+
+comment(// Print first ten bytes of a binary file:)
+keyword(def) method(dumpStart)operator(()ident(filename)(\)) operator({)
+ ident(bytes) operator(=) keyword(new) pre_type(File)operator(()ident(filename)(\))operator(.)ident(newInputStream)operator(()(\))
+ integer(10)operator(.)ident(times)operator({)
+ ident(print) ident(bytes)operator(.)ident(read)operator(()(\)) operator(+) string<delimiter(')content( )delimiter(')>
+ (})
+ ident(println)operator(()(\))
+(})
+ident(dumpStart)operator(()pre_type(System)operator(.)ident(getProperty)operator(()string<delimiter(')content(java.home)delimiter(')>(\))operator(+)string<delimiter(')content(/lib/rt.jar)delimiter(')>(\))
+comment(// => 80 75 3 4 10 0 0 0 0 0 (note first two bytes = PK - you might recognize this)
+comment(// as the starting sequence of a zip file\))
+ident(dumpStart)operator(()string<delimiter(')content(Pleac/classes/pleac8.class)delimiter(')>(\)) comment(// after running groovyc compiler in src directory)
+comment(// => 202 254 186 190 0 0 0 47 2 20 (starting bytes in HEX: CAFEBABE\))
+
+ident(binfile) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/data/temp.bin)delimiter(')>(\))
+ident(binfile)operator(.)ident(withOutputStream)operator({) ident(stream) operator(->) operator(()integer(0)operator(..<)integer(20)(\))operator(.)ident(each)operator({) ident(stream)operator(.)ident(write)operator(()local_variable(it)(\)) (}})
+ident(binfile)operator(.)ident(eachByte)operator({) ident(print) local_variable(it) operator(+) string<delimiter(')content( )delimiter(')> (})operator(;) ident(println)operator(()(\))
+comment(// => 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.12)
+comment(//----------------------------------------------------------------------------------)
+comment(// lets treat binfile as having 5 records of size 4, let's print out the 3rd record)
+ident(recsize) operator(=) integer(4)
+ident(recno) operator(=) integer(2) comment(// index starts at 0)
+ident(address) operator(=) ident(recsize) operator(*) ident(recno)
+ident(randomaccess) operator(=) keyword(new) pre_type(RandomAccessFile)operator(()ident(binfile)operator(,) string<delimiter(')content(r)delimiter(')>(\))
+ident(randomaccess)operator(.)ident(seek)operator(()ident(address)(\))
+ident(recsize)operator(.)ident(times)operator({) ident(print) ident(randomaccess)operator(.)ident(read)operator(()(\)) operator(+) string<delimiter(')content( )delimiter(')> (})operator(;) ident(println)operator(()(\)) comment(// => 8 9 10 11)
+ident(randomaccess)operator(.)ident(close)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// let's take the example from 8.12 but replace the 3rd record with)
+comment(// 90 - the original value in the file)
+comment(// this is an alternative example to the Perl cookbook which is cross platform)
+comment(// see chapter 1 regarding un/pack which could be combined with below)
+comment(// to achieve the full functionality of the original 8.13)
+ident(recsize) operator(=) integer(4)
+ident(recno) operator(=) integer(2) comment(// index starts at 0)
+ident(address) operator(=) ident(recsize) operator(*) ident(recno)
+ident(randomaccess) operator(=) keyword(new) pre_type(RandomAccessFile)operator(()ident(binfile)operator(,) string<delimiter(')content(rw)delimiter(')>(\))
+ident(randomaccess)operator(.)ident(seek)operator(()ident(address)(\))
+ident(bytes) operator(=) type([])
+ident(recsize)operator(.)ident(times)operator({) ident(bytes) operator(+=) ident(randomaccess)operator(.)ident(read)operator(()(\)) (})
+ident(randomaccess)operator(.)ident(seek)operator(()ident(address)(\))
+ident(bytes)operator(.)ident(each)operator({) ident(b) operator(->) ident(randomaccess)operator(.)ident(write)operator(()integer(90) operator(-) ident(b)(\)) (})
+ident(randomaccess)operator(.)ident(close)operator(()(\))
+ident(binfile)operator(.)ident(eachByte)operator({) ident(print) local_variable(it) operator(+) string<delimiter(')content( )delimiter(')> (})operator(;) ident(println)operator(()(\))
+comment(// => 0 1 2 3 4 5 6 7 82 81 80 79 12 13 14 15 16 17 18 19)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.14)
+comment(//----------------------------------------------------------------------------------)
+comment(// reading a String would involve looping and collecting the read bytes)
+
+comment(// simple bgets)
+comment(// this is similar to the revised 8.13 but would look for the terminating 0)
+
+comment(// simplistic strings functionality)
+ident(binfile)operator(.)ident(eachByte)operator({) ident(b) operator(->) keyword(if) operator(()operator(()type(int)(\))ident(b) keyword(in) integer(32)operator(..)integer(126)(\)) ident(print) operator(()operator(()type(char)(\))ident(b)(\)) (})operator(;) ident(println)operator(()(\)) comment(// => RQPO)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.15)
+comment(//----------------------------------------------------------------------------------)
+comment(// You could combine the byte-level reading/writing mechanisms shown)
+comment(// in 8.11 - 8.12 and combine that with the un/pack functionality from)
+comment(// Chapter 1 to achieve the desired functionality. A more Java and Groovy)
+comment(// friendly way to do this would be to use the Scattering and Gathering)
+comment(// stream operations of channels for byte-oriented record fields or)
+comment(// data-oriented records. Alternatively, the dataInput/output stream)
+comment(// capabilities for data-oriented records. Finally, the)
+comment(// objectInput/output stream capabilities could be used for object types.)
+comment(// Note, these examples mix reading and writing even though the original)
+comment(// Perl example was just about reading.)
+
+
+comment(// fixed-length byte-oriented records using channels)
+comment(// typical approach used with low-level protocols or file formats)
+keyword(import) include(java.nio.*)
+ident(binfile)operator(.)ident(delete)operator(()(\))operator(;) ident(binfile)operator(.)ident(createNewFile)operator(()(\)) comment(// start from scratch)
+ident(buf1) operator(=) pre_type(ByteBuffer)operator(.)ident(wrap)operator(()operator([)integer(10)operator(,)integer(11)operator(,)integer(12)operator(,)integer(13)(]) keyword(as) type(byte)type([])(\)) comment(// simulate 4 byte field)
+ident(buf2) operator(=) pre_type(ByteBuffer)operator(.)ident(wrap)operator(()operator([)integer(44)operator(,)integer(45)(]) keyword(as) type(byte)type([])(\)) comment(// 2 byte field)
+ident(buf3) operator(=) pre_type(ByteBuffer)operator(.)ident(wrap)operator(()string<delimiter(')content(Hello)delimiter(')>operator(.)ident(bytes)(\)) comment(// String)
+ident(records) operator(=) operator([)ident(buf1)operator(,) ident(buf2)operator(,) ident(buf3)(]) keyword(as) pre_type(ByteBuffer)type([])
+ident(channel) operator(=) keyword(new) pre_type(FileOutputStream)operator(()ident(binfile)(\))operator(.)ident(channel)
+ident(channel)operator(.)ident(write)operator(()ident(records)(\)) comment(// gathering byte records)
+ident(channel)operator(.)ident(close)operator(()(\))
+ident(binfile)operator(.)ident(eachByte)operator({) ident(print) local_variable(it) operator(+) string<delimiter(')content( )delimiter(')> (})operator(;) ident(println)operator(()(\))
+comment(// => 10 11 12 13 44 45 72 101 108 108 111)
+comment(// ScatteringInputStream would convert this back into an array of byte[])
+
+
+comment(// data-oriented streams using channels)
+ident(binfile)operator(.)ident(delete)operator(()(\))operator(;) ident(binfile)operator(.)ident(createNewFile)operator(()(\)) comment(// start from scratch)
+ident(buf) operator(=) pre_type(ByteBuffer)operator(.)ident(allocate)operator(()integer(24)(\))
+ident(now) operator(=) pre_type(System)operator(.)ident(currentTimeMillis)operator(()(\))
+ident(buf)operator(.)ident(put)operator(()string<delimiter(')content(PI=)delimiter(')>operator(.)ident(bytes)(\))operator(.)ident(putDouble)operator(()pre_type(Math)operator(.)ident(PI)(\))operator(.)ident(put)operator(()string<delimiter(')content(Date=)delimiter(')>operator(.)ident(bytes)(\))operator(.)ident(putLong)operator(()ident(now)(\))
+ident(buf)operator(.)ident(flip)operator(()(\)) comment(// readies for writing: set length and point back to start)
+ident(channel) operator(=) keyword(new) pre_type(FileOutputStream)operator(()ident(binfile)(\))operator(.)ident(channel)
+ident(channel)operator(.)ident(write)operator(()ident(buf)(\))
+ident(channel)operator(.)ident(close)operator(()(\))
+comment(// now read it back in)
+ident(channel) operator(=) keyword(new) pre_type(FileInputStream)operator(()ident(binfile)(\))operator(.)ident(channel)
+ident(buf) operator(=) pre_type(ByteBuffer)operator(.)ident(allocate)operator(()integer(24)(\))
+ident(channel)operator(.)ident(read)operator(()ident(buf)(\))
+ident(buf)operator(.)ident(flip)operator(()(\))
+integer(3)operator(.)ident(times)operator({) ident(print) operator(()operator(()type(char)(\))ident(buf)operator(.)ident(get)operator(()(\)\)) (})
+ident(println) operator(()ident(buf)operator(.)ident(getDouble)operator(()(\)\))
+integer(5)operator(.)ident(times)operator({) ident(print) operator(()operator(()type(char)(\))ident(buf)operator(.)ident(get)operator(()(\)\)) (})
+ident(println) operator(()keyword(new) pre_type(Date)operator(()ident(buf)operator(.)ident(getLong)operator(()(\)\)\))
+ident(channel)operator(.)ident(close)operator(()(\))
+comment(// =>)
+comment(// PI=3.141592653589793)
+comment(// Date=Sat Jan 13 00:14:50 EST 2007)
+
+comment(// object-oriented streams)
+ident(binfile)operator(.)ident(delete)operator(()(\))operator(;) ident(binfile)operator(.)ident(createNewFile)operator(()(\)) comment(// start from scratch)
+type(class) class(Person) directive(implements) pre_type(Serializable) operator({) keyword(def) ident(name)operator(,) ident(age) (})
+ident(binfile)operator(.)ident(withObjectOutputStream)operator({) ident(oos) operator(->)
+ ident(oos)operator(.)ident(writeObject)operator(()keyword(new) ident(Person)operator(()key(name)operator(:)string<delimiter(')content(Bernie)delimiter(')>operator(,)key(age)operator(:)integer(16)(\)\))
+ ident(oos)operator(.)ident(writeObject)operator(()operator([)integer(1)operator(:)string<delimiter(')content(a)delimiter(')>operator(,) integer(2)operator(:)string<delimiter(')content(b)delimiter(')>(]\))
+ ident(oos)operator(.)ident(writeObject)operator(()keyword(new) pre_type(Date)operator(()(\)\))
+(})
+comment(// now read it back in)
+ident(binfile)operator(.)ident(withObjectInputStream)operator({) ident(ois) operator(->)
+ ident(person) operator(=) ident(ois)operator(.)ident(readObject)operator(()(\))
+ ident(println) string<delimiter(")inline<inline_delimiter($)ident(person)>content(.name is )inline<inline_delimiter($)ident(person)>content(.age)delimiter(")>
+ ident(println) ident(ois)operator(.)ident(readObject)operator(()(\))
+ ident(println) ident(ois)operator(.)ident(readObject)operator(()(\))
+(})
+comment(// =>)
+comment(// Bernie is 16)
+comment(// [1:"a", 2:"b"])
+comment(// Sat Jan 13 00:22:13 EST 2007)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.16)
+comment(//----------------------------------------------------------------------------------)
+comment(// use built-in Java property class)
+comment(// suppose you have the following file:)
+comment(// # set your database settings here)
+comment(// server=localhost)
+comment(// url=jdbc:derby:derbyDB;create=true)
+comment(// user.name=me)
+comment(// user.password=secret)
+ident(props) operator(=) keyword(new) pre_type(Properties)operator(()(\))
+ident(propsfile)operator(=)keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/data/plain.properties)delimiter(')>(\))
+ident(props)operator(.)ident(load)operator(()ident(propsfile)operator(.)ident(newInputStream)operator(()(\)\))
+ident(props)operator(.)ident(list)operator(()pre_type(System)operator(.)ident(out)(\))
+comment(// =>)
+comment(// -- listing properties --)
+comment(// user.name=me)
+comment(// user.password=secret)
+comment(// url=jdbc:derby:derbyDB;create=true)
+comment(// server=localhost)
+
+comment(// There are also provisions for writing properties file.)
+
+comment(// (additional example to Perl\))
+comment(// You can also read and write xml properties files.)
+keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/data/props.xml)delimiter(')>(\))operator(.)ident(withOutputStream)operator({) ident(os) operator(->)
+ ident(props)operator(.)ident(storeToXML)operator(()ident(os)operator(,) string<delimiter(")content(Database Settings)delimiter(")>(\))
+(})
+comment(// =>)
+comment(// <?xml version="1.0" encoding="UTF-8"?>)
+comment(// <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">)
+comment(// <properties>)
+comment(// <comment>Database Settings</comment>)
+comment(// <entry key="user.password">secret</entry>)
+comment(// <entry key="user.name">me</entry>)
+comment(// <entry key="url">jdbc:derby:derbyDB;create=true</entry>)
+comment(// <entry key="server">localhost</entry>)
+comment(// </properties>)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.17)
+comment(//----------------------------------------------------------------------------------)
+comment(// The File class provides canRead(\), canWrite(\) and canExecute(\) (JDK6\) methods)
+comment(// for finding out about security information specific to the user. JSR 203)
+comment(// (expected in Java 7\) provides access to additional security related attributes.)
+
+comment(// Another useful package to use when wondering about the trustworthiness of a)
+comment(// file is the java.security package. It contains many classes. Just one is)
+comment(// MessageDigest. This would allow you to create a strong checksum of a file.)
+comment(// Your program could refuse to operate if a file it was accessing didn't have the)
+comment(// checksum it was expecting - an indication that it may have been tampered with.)
+
+comment(// (additional info\))
+comment(// While getting file-based security permissions correct is important, it isn't the)
+comment(// only mechanism to use for security when using Java based systems. Java provides)
+comment(// policy files and an authorization and authentication API which lets you secure)
+comment(// any reources (not just files\) at various levels of granularity with various)
+comment(// security mechanisms.)
+comment(// Security policies may be universal, apply to a particular codebase, or)
+comment(// using JAAS apply to individuals. Some indicative policy statements:)
+comment(// grant {)
+comment(// permission java.net.SocketPermission "*", "connect";)
+comment(// permission java.io.FilePermission "C:\\\\users\\\\cathy\\\\foo.bat", "read";)
+comment(// };)
+comment(// grant codebase "file:./*", Principal ExamplePrincipal "Secret" {)
+comment(// permission java.io.FilePermission "dummy.txt", "read";)
+comment(// };)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.18)
+comment(//----------------------------------------------------------------------------------)
+comment(// general purpose utility methods)
+keyword(def) method(getString)operator(()ident(buf)operator(,)ident(size)(\))operator({)
+ comment(// consider get(buf[]\) instead of get(buf\) for efficiency)
+ ident(b)operator(=)type([])operator(;) ident(size)operator(.)ident(times)operator({)ident(b)operator(+=)ident(buf)operator(.)ident(get)operator(()(\)})operator(;) keyword(new) pre_type(String)operator(()ident(b) keyword(as) type(byte)type([])(\))operator(.)ident(trim)operator(()(\))
+(})
+keyword(def) method(getInt)operator(()ident(buf)operator(,)ident(size)(\)) operator({)
+ comment(// normally in Java we would just use methods like getLong(\))
+ comment(// to read a long but wish to ignore platform issues here)
+ type(long) ident(val) operator(=) integer(0)
+ keyword(for) operator(()ident(n) keyword(in) integer(0)operator(..<)ident(size)(\)) operator({) ident(val) operator(+=) operator(()operator(()type(int)(\))ident(buf)operator(.)ident(get)operator(()(\)) operator(&) hex(0xFF)(\)) operator(<)operator(<) operator(()ident(n) operator(*) integer(8)(\)) (})
+ keyword(return) ident(val)
+(})
+keyword(def) method(getDate)operator(()ident(buf)(\)) operator({)
+ keyword(return) keyword(new) pre_type(Date)operator(()ident(getInt)operator(()ident(buf)operator(,)integer(4)(\)) operator(*) integer(1000)(\)) comment(// Java uses millis)
+(})
+
+comment(// specific utility method (wtmp file from ubuntu 6.10\))
+keyword(def) method(processWtmpRecords)operator(()ident(file)operator(,) ident(origpos)(\)) operator({)
+ ident(channel) operator(=) keyword(new) pre_type(RandomAccessFile)operator(()ident(file)operator(,) string<delimiter(')content(r)delimiter(')>(\))operator(.)ident(channel)
+ ident(recsize) operator(=) integer(4) operator(+) integer(4) operator(+) integer(32) operator(+) integer(4) operator(+) integer(32) operator(+) integer(256) operator(+) integer(8) operator(+) integer(4) operator(+) integer(40)
+ ident(channel)operator(.)ident(position)operator(()ident(origpos)(\))
+ ident(newpos) operator(=) ident(origpos)
+ ident(buf) operator(=) pre_type(ByteBuffer)operator(.)ident(allocate)operator(()ident(recsize)(\))
+ keyword(while) operator(()operator(()ident(count) operator(=) ident(channel)operator(.)ident(read)operator(()ident(buf)(\)\)) operator(!=) operator(-)integer(1)(\)) operator({)
+ keyword(if) operator(()ident(count) operator(!=) ident(recsize)(\)) keyword(break)
+ ident(buf)operator(.)ident(flip)operator(()(\))
+ ident(print) ident(getInt)operator(()ident(buf)operator(,)integer(4)(\)) operator(+) string<delimiter(')content( )delimiter(')> comment(// type)
+ ident(print) ident(getInt)operator(()ident(buf)operator(,)integer(4)(\)) operator(+) string<delimiter(')content( )delimiter(')> comment(// pid)
+ ident(print) ident(getString)operator(()ident(buf)operator(,)integer(32)(\)) operator(+) string<delimiter(')content( )delimiter(')> comment(// line)
+ ident(print) ident(getString)operator(()ident(buf)operator(,)integer(4)(\)) operator(+) string<delimiter(')content( )delimiter(')> comment(// inittab)
+ ident(print) ident(getString)operator(()ident(buf)operator(,)integer(32)(\)) operator(+) string<delimiter(')content( )delimiter(')> comment(// user)
+ ident(print) ident(getString)operator(()ident(buf)operator(,)integer(256)(\)) operator(+) string<delimiter(')content( )delimiter(')> comment(// hostname)
+ ident(buf)operator(.)ident(position)operator(()ident(buf)operator(.)ident(position)operator(()(\)) operator(+) integer(8)(\)) comment(// skip)
+ ident(println) string<delimiter(")inline<inline_delimiter(${)ident(getDate(buf\))inline_delimiter(})>content( )delimiter(")> comment(// time)
+ ident(buf)operator(.)ident(clear)operator(()(\))
+ ident(newpos) operator(=) ident(channel)operator(.)ident(position)operator(()(\))
+ (})
+ keyword(return) ident(newpos)
+(})
+
+ident(wtmp) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/data/wtmp)delimiter(')>(\))
+comment(// wtmpTailingScript:)
+ident(sampleInterval) operator(=) integer(2000) comment(// 2000 millis = 2 secs)
+ident(filePointer) operator(=) ident(wtmp)operator(.)ident(size)operator(()(\)) comment(// begin tailing from the end of the file)
+keyword(while)operator(()keyword(true)(\)) operator({)
+ comment(// Compare the length of the file to the file pointer)
+ type(long) ident(fileLength) operator(=) ident(wtmp)operator(.)ident(size)operator(()(\))
+ keyword(if)operator(() ident(fileLength) operator(>) ident(filePointer) (\)) operator({)
+ comment(// There is data to read)
+ ident(filePointer) operator(=) ident(processWtmpRecords)operator(()ident(wtmp)operator(,) ident(filePointer)(\))
+ (})
+ comment(// Sleep for the specified interval)
+ pre_type(Thread)operator(.)ident(sleep)operator(() ident(sampleInterval) (\))
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.19)
+comment(//----------------------------------------------------------------------------------)
+comment(// contains most of the functionality of the original (not guaranteed to be perfect\))
+comment(// -i ignores errors, e.g. if one target is write protected, the others will work)
+comment(// -u writes files in unbuffered mode (ignore for '|'\))
+comment(// -n not to stdout)
+comment(// -a all files are in append mode)
+comment(// '>>file1' turn on append for individual file)
+comment(// '|wc' or '|grep x' etc sends output to forked process (only one at any time\))
+type(class) class(MultiStream) operator({)
+ directive(private) ident(targets)
+ directive(private) ident(ignoreErrors)
+ ident(MultiStream)operator(()pre_type(List) ident(targets)operator(,) ident(ignore)(\)) operator({)
+ local_variable(this)operator(.)ident(targets) operator(=) ident(targets)
+ ident(ignoreErrors) operator(=) ident(ignore)
+ (})
+ keyword(def) method(println)operator(()pre_type(String) ident(content)(\)) operator({)
+ ident(targets)operator(.)ident(each)operator({)
+ keyword(try) operator({)
+ local_variable(it)operator(?)operator(.)ident(write)operator(()ident(content)operator(.)ident(bytes)(\))
+ (}) keyword(catch) operator(()pre_type(Exception) ident(ex)(\)) operator({)
+ keyword(if) operator(()operator(!)ident(ignoreErrors)(\)) keyword(throw) ident(ex)
+ ident(targets) operator(-=) local_variable(it)
+ local_variable(it)operator(?)operator(.)ident(close)operator(()(\))
+ (})
+ (})
+ (})
+ keyword(def) method(close)operator(()(\)) operator({) ident(targets)operator(.)ident(each)operator({) local_variable(it)operator(?)operator(.)ident(close)operator(()(\)) (}) (})
+(})
+
+type(class) class(TeeTarget) operator({)
+ directive(private) ident(filename)
+ directive(private) ident(stream)
+ directive(private) ident(p)
+
+ ident(TeeTarget)operator(()pre_type(String) ident(name)operator(,) ident(append)operator(,) ident(buffered)operator(,) ident(ignore)(\)) operator({)
+ keyword(if) operator(()ident(name)operator(.)ident(startsWith)operator(()string<delimiter(')content(>>)delimiter(')>(\)\)) operator({)
+ ident(createFileStream)operator(()ident(name)operator([)integer(2)operator(..)operator(-)integer(1)(])operator(,)keyword(true)operator(,)ident(buffered)operator(,)ident(ignore)(\))
+ (}) keyword(else) keyword(if) operator(()ident(name)operator(.)ident(startsWith)operator(()string<delimiter(')content(|)delimiter(')>(\)\)) operator({)
+ ident(createProcessReader)operator(()ident(name)operator([)integer(1)operator(..)operator(-)integer(1)(]\))
+ (}) keyword(else) operator({)
+ ident(createFileStream)operator(()ident(name)operator(,)ident(append)operator(,)ident(buffered)operator(,)ident(ignore)(\))
+ (})
+ (})
+
+ ident(TeeTarget)operator(()pre_type(OutputStream) ident(stream)(\)) operator({) local_variable(this)operator(.)ident(stream) operator(=) ident(stream) (})
+
+ keyword(def) method(write)operator(()ident(bytes)(\)) operator({) ident(stream)operator(?)operator(.)ident(write)operator(()ident(bytes)(\)) (})
+ keyword(def) method(close)operator(()(\)) operator({) ident(stream)operator(?)operator(.)ident(close)operator(()(\)) (})
+
+ directive(private) ident(createFileStream)operator(()ident(name)operator(,) ident(append)operator(,) ident(buffered)operator(,) ident(ignore)(\)) operator({)
+ ident(filename) operator(=) ident(name)
+ keyword(def) ident(fos)
+ keyword(try) operator({)
+ ident(fos) operator(=) keyword(new) pre_type(FileOutputStream)operator(()ident(name)operator(,) ident(append)(\))
+ (}) keyword(catch) operator(()pre_type(Exception) ident(ex)(\)) operator({)
+ keyword(if) operator(()ident(ignore)(\)) keyword(return)
+ (})
+ keyword(if) operator(()operator(!)ident(buffered)(\)) ident(stream) operator(=) ident(fos)
+ keyword(else) ident(stream) operator(=) keyword(new) pre_type(BufferedOutputStream)operator(()ident(fos)(\))
+ (})
+ directive(private) ident(createWriter)operator(()ident(os)(\)) operator({)keyword(new) pre_type(PrintWriter)operator(()keyword(new) pre_type(BufferedOutputStream)operator(()ident(os)(\)\)})
+ directive(private) ident(createReader)operator(()ident(is)(\)) operator({)keyword(new) pre_type(BufferedReader)operator(()keyword(new) pre_type(InputStreamReader)operator(()ident(is)(\)\)})
+ directive(private) ident(createPiperThread)operator(()ident(br)operator(,) ident(pw)(\)) operator({)
+ pre_type(Thread)operator(.)ident(start)operator({)
+ keyword(def) ident(next)
+ keyword(while)operator(()operator(()ident(next) operator(=) ident(br)operator(.)ident(readLine)operator(()(\)\))operator(!=)keyword(null)(\)) operator({)
+ ident(pw)operator(.)ident(println)operator(()ident(next)(\))
+ (})
+ ident(pw)operator(.)ident(flush)operator(()(\))operator(;) ident(pw)operator(.)ident(close)operator(()(\))
+ (})
+ (})
+ directive(private) ident(createProcessReader)operator(()ident(name)(\)) operator({)
+ keyword(def) ident(readFromStream) operator(=) keyword(new) pre_type(PipedInputStream)operator(()(\))
+ keyword(def) ident(r1) operator(=) ident(createReader)operator(()ident(readFromStream)(\))
+ ident(stream) operator(=) keyword(new) pre_type(BufferedOutputStream)operator(()keyword(new) pre_type(PipedOutputStream)operator(()ident(readFromStream)(\)\))
+ ident(p) operator(=) pre_type(Runtime)operator(.)ident(runtime)operator(.)ident(exec)operator(()ident(name)(\))
+ keyword(def) ident(w1) operator(=) ident(createWriter)operator(()ident(p)operator(.)ident(outputStream)(\))
+ ident(createPiperThread)operator(()ident(r1)operator(,) ident(w1)(\))
+ keyword(def) ident(w2) operator(=) ident(createWriter)operator(()pre_type(System)operator(.)ident(out)(\))
+ keyword(def) ident(r2) operator(=) ident(createReader)operator(()ident(p)operator(.)ident(inputStream)(\))
+ ident(createPiperThread)operator(()ident(r2)operator(,) ident(w2)(\))
+ (})
+(})
+
+ident(targets) operator(=) type([])
+ident(append) operator(=) keyword(false)operator(;) ident(ignore) operator(=) keyword(false)operator(;) ident(includeStdout) operator(=) keyword(true)operator(;) ident(buffer) operator(=) keyword(true)
+operator(()integer(0)operator(..<)ident(args)operator(.)ident(size)operator(()(\)\))operator(.)ident(each)operator({)
+ ident(arg) operator(=) ident(args)operator([)local_variable(it)(])
+ keyword(if) operator(()ident(arg)operator(.)ident(startsWith)operator(()string<delimiter(')content(-)delimiter(')>(\)\)) operator({)
+ keyword(switch) operator(()ident(arg)(\)) operator({)
+ keyword(case) string<delimiter(')content(-a)delimiter(')>operator(:) ident(append) operator(=) keyword(true)operator(;) keyword(break)
+ keyword(case) string<delimiter(')content(-i)delimiter(')>operator(:) ident(ignore) operator(=) keyword(true)operator(;) keyword(break)
+ keyword(case) string<delimiter(')content(-n)delimiter(')>operator(:) ident(includeStdout) operator(=) keyword(false)operator(;) keyword(break)
+ keyword(case) string<delimiter(')content(-u)delimiter(')>operator(:) ident(buffer) operator(=) keyword(false)operator(;) keyword(break)
+ keyword(default)operator(:)
+ ident(println) string<delimiter(")content(usage: tee [-ainu] [filenames] ...)delimiter(")>
+ pre_type(System)operator(.)ident(exit)operator(()integer(1)(\))
+ (})
+ (}) keyword(else) ident(targets) operator(+=) ident(arg)
+(})
+ident(targets) operator(=) ident(targets)operator(.)ident(collect)operator({) keyword(new) ident(TeeTarget)operator(()local_variable(it)operator(,) ident(append)operator(,) ident(buffer)operator(,) ident(ignore)(\)) (})
+keyword(if) operator(()ident(includeStdout)(\)) ident(targets) operator(+=) keyword(new) ident(TeeTarget)operator(()pre_type(System)operator(.)ident(out)(\))
+keyword(def) ident(tee) operator(=) keyword(new) ident(MultiStream)operator(()ident(targets)operator(,) ident(ignore)(\))
+keyword(while) operator(()ident(line) operator(=) pre_type(System)operator(.)ident(in)operator(.)ident(readLine)operator(()(\)\)) operator({)
+ ident(tee)operator(.)ident(println)operator(()ident(line)(\))
+(})
+ident(tee)operator(.)ident(close)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_8.20)
+comment(//----------------------------------------------------------------------------------)
+comment(// most of the functionality - uses an explicit uid - ran on ubuntu 6.10 on intel)
+ident(lastlog) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/data/lastlog)delimiter(')>(\))
+ident(channel) operator(=) keyword(new) pre_type(RandomAccessFile)operator(()ident(lastlog)operator(,) string<delimiter(')content(r)delimiter(')>(\))operator(.)ident(channel)
+ident(uid) operator(=) integer(1000)
+ident(recsize) operator(=) integer(4) operator(+) integer(32) operator(+) integer(256)
+ident(channel)operator(.)ident(position)operator(()ident(uid) operator(*) ident(recsize)(\))
+ident(buf) operator(=) pre_type(ByteBuffer)operator(.)ident(allocate)operator(()ident(recsize)(\))
+ident(channel)operator(.)ident(read)operator(()ident(buf)(\))
+ident(buf)operator(.)ident(flip)operator(()(\))
+ident(date) operator(=) ident(getDate)operator(()ident(buf)(\))
+ident(line) operator(=) ident(getString)operator(()ident(buf)operator(,)integer(32)(\))
+ident(host) operator(=) ident(getString)operator(()ident(buf)operator(,)integer(256)(\))
+ident(println) string<delimiter(")content(User with uid )inline<inline_delimiter($)ident(uid)>content( last logged on )inline<inline_delimiter($)ident(date)>content( from )inline<inline_delimiter(${)ident(host?host:'unknown')inline_delimiter(})>content( on )inline<inline_delimiter($)ident(line)>delimiter(")>
+comment(// => User with uid 1000 last logged on Sat Jan 13 09:09:35 EST 2007 from unknown on :0)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_9.0)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy builds on Java's file and io classes which provide an operating)
+comment(// system independent abstraction of a file system. The actual File class)
+comment(// is the main class of interest. It represents a potential file or)
+comment(// directory - which may or may not (yet\) exist. In versions of Java up to)
+comment(// and including Java 6, the File class was missing some of the functionality)
+comment(// required to implement some of the examples in the Chapter (workarounds)
+comment(// and alternatives are noted below\). In Java 7, (also known as "Dolphin"\))
+comment(// new File abstraction facilities are being worked on but haven't yet been)
+comment(// publically released. These new features are known as JSR 203 and are)
+comment(// referred to when relevant to some of the examples. Thanks to Alan Bateman)
+comment(// from Sun for clarification regarding various aspects of JSR 203. Apologies)
+comment(// if I misunderstood any aspects relayed to me and also usual disclaimers)
+comment(// apply regarding features which may change or be dropped before release.)
+
+comment(// path='/usr/bin'; file='vi' // linux/mac os?)
+ident(path)operator(=)string<delimiter(')content(C:/windows)delimiter(')>operator(;) ident(file)operator(=)string<delimiter(')content(explorer.exe)delimiter(')> comment(// windows)
+ident(entry) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(")inline<inline_delimiter($)ident(path)>delimiter(")>(\))
+keyword(assert) ident(entry)operator(.)ident(isDirectory)operator(()(\))
+ident(entry) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(")inline<inline_delimiter($)ident(path)>content(/)inline<inline_delimiter($)ident(file)>delimiter(")>(\))
+keyword(assert) ident(entry)operator(.)ident(isFile)operator(()(\))
+
+ident(println) pre_type(File)operator(.)ident(separator)
+comment(// => \\ (on Windows\))
+comment(// => / (on Unix\))
+comment(// however if you just stick to backslashes Java converts for you)
+comment(// in most situations)
+
+comment(// File modification time (no exact equivalent of ctime - but you can)
+comment(// call stat(\) using JNI or use exec(\) of dir or ls to get this kind of info\))
+comment(// JSR 203 also plans to provide such info in Java 7.)
+ident(println) keyword(new) pre_type(Date)operator(()ident(entry)operator(.)ident(lastModified)operator(()(\)\))
+comment(// => Wed Aug 04 07:00:00 EST 2004)
+
+comment(// file size)
+ident(println) ident(entry)operator(.)ident(size)operator(()(\))
+comment(// => 1032192)
+
+comment(// check if we have permission to read the file)
+keyword(assert) ident(entry)operator(.)ident(canRead)operator(()(\))
+
+comment(// check if file is binary or text?)
+comment(// There is no functionality for this at the file level.)
+comment(// Java has the Java Activation Framework (jaf\) which is used to)
+comment(// associate files (and streams\) with MIME Types and subsequently)
+comment(// binary data streams or character encodings for (potentially)
+comment(// multilanguage\) text files. JSR-203 provides a method to determine)
+comment(// the MIME type of a file. Depending on the platform the file type may)
+comment(// be determined based on a file attribute, file name "extension", the)
+comment(// bytes of the files (byte sniffing\) or other means. It is service)
+comment(// provider based so developers can plug in their own file type detection)
+comment(// mechanisms as required. "Out of the box" it will ship with file type)
+comment(// detectors that are appropriate for the platform (integrates with GNOME,)
+comment(// Windows registry, etc.\).)
+
+comment(// Groovy uses File for directories and files)
+comment(// displayAllFilesInUsrBin:)
+keyword(new) pre_type(File)operator(()string<delimiter(')content(/usr/bin)delimiter(')>(\))operator(.)ident(eachFile)operator({) ident(file) operator(->)
+ ident(println) string<delimiter(")content(Inside /usr/bin is something called )inline<inline_delimiter($)ident(file)>content(.name)delimiter(")>
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_9.1)
+comment(//----------------------------------------------------------------------------------)
+ident(file) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(")content(filename)delimiter(")>(\))
+ident(file) operator(<)operator(<) string<delimiter(')content(hi)delimiter(')>
+ident(timeModified) operator(=) ident(file)operator(.)ident(lastModified)operator(()(\))
+ident(println) keyword(new) pre_type(Date)operator(()ident(timeModified)(\))
+comment(// => Sun Jan 07 11:49:02 EST 2007)
+
+ident(MILLIS_PER_WEEK) operator(=) integer(60) operator(*) integer(60) operator(*) integer(24) operator(*) integer(1000) operator(*) integer(7)
+ident(file)operator(.)ident(setLastModified)operator(()ident(timeModified) operator(-) ident(MILLIS_PER_WEEK)(\))
+ident(println) keyword(new) pre_type(Date)operator(()ident(file)operator(.)ident(lastModified)operator(()(\)\))
+comment(// => Sun Dec 31 11:49:02 EST 2006)
+
+comment(// Java currently doesn't provide access to other timestamps but)
+comment(// there are things that can be done:)
+comment(// (1\) You can use JNI to call to C, e.g. stat(\))
+comment(// (2\) Use exec(\) and call another program, e.g. dir, ls, ... to get the value you are after)
+comment(// (3\) Here is a Windows specific patch to get lastAccessedTime and creationTime)
+comment(// http://forum.java.sun.com/thread.jspa?forumID=31&start=0&threadID=409921&range=100#1800193)
+comment(// (4\) There is an informal patch for Java 5/6 which gives lastAccessedTime on Windows and Linux)
+comment(// and creationTime on windows:)
+comment(// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6314708)
+comment(// (5\) JSR 203 (currently targetted for Java 7\) aims to provide)
+comment(// "bulk access to file attributes, change notification, escape to filesystem-specific APIs")
+comment(// this is supposed to include creationTime and lastAccessedTime along with many)
+comment(// security-related file attributes)
+
+comment(// viFileWithoutChangingModificationTimeScript:)
+doctype(#!/usr/bin/groovy)
+comment(// uvi - vi a file without changing it's last modified time)
+keyword(if) operator(()ident(args)operator(.)ident(size)operator(()(\)) operator(!=) integer(1)(\))
+ ident(println) string<delimiter(")content(usage: uvi filename)delimiter(")>
+ pre_type(System)operator(.)ident(exit)operator(()integer(1)(\))
+(})
+ident(file) operator(=) ident(args)operator([)integer(0)(])
+ident(origTime) operator(=) keyword(new) pre_type(File)operator(()ident(file)(\))operator(.)ident(lastModified)operator(()(\))
+string<delimiter(")content(vi )inline<inline_delimiter($)ident(file)>delimiter(")>operator(.)ident(execute)operator(()(\))
+keyword(new) pre_type(File)operator(()ident(file)(\))operator(.)ident(setLastModified)operator(()ident(origTime)(\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_9.2)
+comment(//----------------------------------------------------------------------------------)
+ident(println) keyword(new) pre_type(File)operator(()string<delimiter(')content(/doesnotexist)delimiter(')>(\))operator(.)ident(exists)operator(()(\)) comment(// => false)
+ident(println) keyword(new) pre_type(File)operator(()string<delimiter(')content(/doesnotexist)delimiter(')>(\))operator(.)ident(delete)operator(()(\)) comment(// => false)
+
+keyword(new) pre_type(File)operator(()string<delimiter(')content(/createme)delimiter(')>(\)) operator(<)operator(<) string<delimiter(')content(Hi there)delimiter(')>
+ident(println) keyword(new) pre_type(File)operator(()string<delimiter(')content(/createme)delimiter(')>(\))operator(.)ident(exists)operator(()(\)) comment(// => true)
+ident(println) keyword(new) pre_type(File)operator(()string<delimiter(')content(/createme)delimiter(')>(\))operator(.)ident(delete)operator(()(\)) comment(// => true)
+
+ident(names) operator(=) operator([)string<delimiter(')content(file1)delimiter(')>operator(,)string<delimiter(')content(file2)delimiter(')>operator(,)string<delimiter(')content(file3)delimiter(')>(])
+ident(files) operator(=) ident(names)operator(.)ident(collect)operator({) keyword(new) pre_type(File)operator(()local_variable(it)(\)) (})
+comment(// create 2 of the files)
+ident(files)operator([)integer(0)operator(..)integer(1)(])operator(.)ident(each)operator({) ident(f) operator(->) ident(f) operator(<)operator(<) ident(f)operator(.)ident(name) (})
+
+keyword(def) method(deleteFiles)operator(()ident(files)(\)) operator({)
+ keyword(def) ident(problemFileNames) operator(=) type([])
+ ident(files)operator(.)ident(each)operator({) ident(f) operator(->)
+ keyword(if) operator(()operator(!)ident(f)operator(.)ident(delete)operator(()(\)\))
+ ident(problemFileNames) operator(+=) ident(f)operator(.)ident(name)
+ (})
+ keyword(def) ident(delCnt) operator(=) ident(files)operator(.)ident(size)operator(()(\)) operator(-) ident(problemFileNames)operator(.)ident(size)operator(()(\))
+ ident(println) string<delimiter(")content(Successfully deleted )inline<inline_delimiter($)ident(delCnt)>content( of )inline<inline_delimiter(${)ident(files.size(\))inline_delimiter(})>content( file(s\))delimiter(")>
+ keyword(if) operator(()ident(problemFileNames)(\))
+ ident(println) string<delimiter(")content(Problems file(s\): )delimiter(")> operator(+) ident(problemFileNames)operator(.)ident(join)operator(()string<delimiter(')content(, )delimiter(')>(\))
+(})
+
+ident(deleteFiles)operator(()ident(files)(\))
+comment(// =>)
+comment(// Successfully deleted 2 of 3 file(s\))
+comment(// Problems file(s\): file3)
+
+comment(// we can also set files for deletion on exit)
+ident(tempFile) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(/xxx)delimiter(')>(\))
+keyword(assert) operator(!)ident(tempFile)operator(.)ident(exists)operator(()(\))
+ident(tempFile) operator(<)operator(<) string<delimiter(')content(junk)delimiter(')>
+keyword(assert) ident(tempFile)operator(.)ident(exists)operator(()(\))
+ident(tempFile)operator(.)ident(deleteOnExit)operator(()(\))
+keyword(assert) ident(tempFile)operator(.)ident(exists)operator(()(\))
+comment(// To confirm this is working, run these steps multiple times in a row.)
+
+comment(// Discussion:)
+comment(// Be careful with deleteOnExit(\) as there is no way to cancel it.)
+comment(// There are also mechanisms specifically for creating unqiuely named temp files.)
+comment(// On completion of JSR 203, there will be additional methods available for)
+comment(// deleting which throw exceptions with detailed error messages rather than)
+comment(// just return booleans.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_9.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// (1\) Copy examples)
+
+comment(//shared setup)
+ident(dummyContent) operator(=) string<delimiter(')content(some content)delimiter(')> operator(+) pre_type(System)operator(.)ident(getProperty)operator(()string<delimiter(')content(line.separator)delimiter(')>(\))
+ident(setUpFromFile)operator(()(\))
+ident(setUpToFile)operator(()(\))
+
+comment(// built-in copy via memory (text files only\))
+ident(to) operator(<)operator(<) ident(from)operator(.)ident(text)
+ident(checkSuccessfulCopyAndDelete)operator(()(\))
+
+comment(// built-in as a stream (text or binary\) with optional encoding)
+ident(to) operator(<)operator(<) ident(from)operator(.)ident(asWritable)operator(()string<delimiter(')content(US-ASCII)delimiter(')>(\))
+ident(checkSuccessfulCopyAndDelete)operator(()(\))
+
+comment(// built-in using AntBuilder)
+comment(// for options, see: http://ant.apache.org/manual/CoreTasks/copy.html)
+keyword(new) ident(AntBuilder)operator(()(\))operator(.)ident(copy)operator(() key(file)operator(:) ident(from)operator(.)ident(canonicalPath)operator(,) key(tofile)operator(:) ident(to)operator(.)ident(canonicalPath) (\))
+ident(checkSuccessfulCopyAndDelete)operator(()(\))
+comment(// =>)
+comment(// [copy] Copying 1 file to D:\\
+)
+
+comment(// use Apache Jakarta Commons IO (jakarta.apache.org\))
+keyword(import) include(org.apache.commons.io.FileUtils)
+comment(// Copies a file to a new location preserving the lastModified date.)
+ident(FileUtils)operator(.)ident(copyFile)operator(()ident(from)operator(,) ident(to)(\))
+ident(checkSuccessfulCopyAndDelete)operator(()(\))
+
+comment(// using execute(\))
+comment(// "cp $from.canonicalPath $to.canonicalPath".execute(\) // unix)
+ident(println) string<delimiter(")content(cmd /c )char(\\")content(copy )inline<inline_delimiter($)ident(from)>content(.canonicalPath )inline<inline_delimiter($)ident(to)>content(.canonicalPath)char(\\")delimiter(")>operator(.)ident(execute)operator(()(\))operator(.)ident(text) comment(// dos vms)
+ident(checkSuccessfulCopyAndDelete)operator(()(\))
+comment(// =>)
+comment(// 1 file(s\) copied.)
+
+comment(// (2\) Move examples)
+comment(// You can just do copy followed by delete but many OS's can just 'rename' in place)
+comment(// so you can additionally do using Java's functionality:)
+keyword(assert) ident(from)operator(.)ident(renameTo)operator(()ident(to)(\))
+keyword(assert) operator(!)ident(from)operator(.)ident(exists)operator(()(\))
+ident(checkSuccessfulCopyAndDelete)operator(()(\))
+comment(// whether renameTo succeeds if from and to are on different platforms)
+comment(// or if to pre-exists is OS dependent, so you should check the return boolean)
+
+comment(// alternatively, Ant has a move task:)
+comment(// http://ant.apache.org/manual/CoreTasks/move.html)
+
+comment(//helper methods)
+keyword(def) method(checkSuccessfulCopyAndDelete)operator(()(\)) operator({)
+ keyword(assert) ident(to)operator(.)ident(text) operator(==) ident(dummyContent)
+ keyword(assert) ident(to)operator(.)ident(delete)operator(()(\))
+ keyword(assert) operator(!)ident(to)operator(.)ident(exists)operator(()(\))
+(})
+keyword(def) method(setUpFromFile)operator(()(\)) operator({)
+ ident(from) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(/from.txt)delimiter(')>(\)) comment(// just a name)
+ ident(from) operator(<)operator(<) ident(dummyContent) comment(// now its a real file with content)
+ ident(from)operator(.)ident(deleteOnExit)operator(()(\)) comment(// that will be deleted on exit)
+(})
+keyword(def) method(setUpToFile)operator(()(\)) operator({)
+ ident(to) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(C:/to.txt)delimiter(')>(\)) comment(// target name)
+ ident(to)operator(.)ident(delete)operator(()(\)) comment(// ensure not left from previous aborted run)
+ keyword(assert) operator(!)ident(to)operator(.)ident(exists)operator(()(\)) comment(// double check)
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_9.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy (because of its Java heritage\) doesn't have an exact)
+comment(// equivalent of stat - as per 9.2 there are numerous mechanisms)
+comment(// to achieve the equivalent, in particular, JSR203 (still in draft\))
+comment(// has specific SymLink support including a FileId class in the)
+comment(// java.nio.filesystems package. This will allow (depending on the)
+comment(// operating system capabilities\) files to be uniquely identified.)
+comment(// If you work on Unix or Linux then you'll recognize this as it device/inode.)
+
+comment(// If you are not interested in the above workarounds/future features)
+comment(// and you are on a unix system, you can compare the absolutePath and)
+comment(// canonicalPath attributes for a file. If they are different it is)
+comment(// a symbolic link. On other operating systems, this difference is not)
+comment(// to be relied upon and even on *nix systems, this will only get you)
+comment(// so far and will also be relatively expensive resource and timewise.)
+
+comment(// process only unique files)
+ident(seen) operator(=) type([])
+keyword(def) method(myProcessing)operator(()ident(file)(\)) operator({)
+ keyword(def) ident(path) operator(=) ident(file)operator(.)ident(canonicalPath)
+ keyword(if) operator(()operator(!)ident(seen)operator(.)ident(contains)operator(()ident(path)(\)\)) operator({)
+ ident(seen) operator(<)operator(<) ident(path)
+ comment(// do something with file because we haven't seen it before)
+ (})
+(})
+
+comment(// find linked files)
+ident(seen) operator(=) operator([)operator(:)(])
+ident(filenames) operator(=) operator([)string<delimiter(')content(/dummyfile1.txt)delimiter(')>operator(,)string<delimiter(')content(/test.lnk)delimiter(')>operator(,)string<delimiter(')content(/dummyfile2.txt)delimiter(')>(])
+ident(filenames)operator(.)ident(each)operator({) ident(filename) operator(->)
+ keyword(def) ident(file) operator(=) keyword(new) pre_type(File)operator(()ident(filename)(\))
+ keyword(def) ident(cpath) operator(=) ident(file)operator(.)ident(canonicalPath)
+ keyword(if) operator(()operator(!)ident(seen)operator(.)ident(containsKey)operator(()ident(cpath)(\)\)) operator({)
+ ident(seen)operator([)ident(cpath)(]) operator(=) type([])
+ (})
+ ident(seen)operator([)ident(cpath)(]) operator(+=) ident(file)operator(.)ident(absolutePath)
+(})
+
+ident(println) string<delimiter(')content(Files with links:)delimiter(')>
+ident(println) ident(seen)operator(.)ident(findAll)operator({) ident(k)operator(,)ident(v) operator(->) ident(v)operator(.)ident(size)operator(()(\)) operator(>) integer(1) (})
+comment(//---------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_9.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// general pattern is:)
+comment(// new File('dirname'\).eachFile{ /* do something ... */ })
+
+comment(// setup (change this on your system\))
+ident(basedir) operator(=) string<delimiter(')content(Pleac/src)delimiter(')>
+
+comment(// process all files printing out full name (. and .. auto excluded\))
+keyword(new) pre_type(File)operator(()ident(basedir)(\))operator(.)ident(eachFile)operator({) ident(f)operator(->)
+ keyword(if) operator(()ident(f)operator(.)ident(isFile)operator(()(\)\)) ident(println) ident(f)operator(.)ident(canonicalPath)
+(})
+comment(// also remove dot files such as '.svn' and '.cvs' etc.)
+keyword(new) pre_type(File)operator(()ident(basedir)(\))operator(.)ident(eachFileMatch)operator(()operator(~)string<delimiter(')content(^[^.].*)delimiter(')>(\))operator({) ident(f)operator(->)
+ keyword(if) operator(()ident(f)operator(.)ident(isFile)operator(()(\)\)) ident(println) ident(f)operator(.)ident(canonicalPath)
+(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_9.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// Globbing via Apache Jakarta ORO)
+keyword(import) include(org.apache.oro.io.GlobFilenameFilter)
+ident(dir) operator(=) keyword(new) pre_type(File)operator(()ident(basedir)(\))
+ident(namelist) operator(=) ident(dir)operator(.)ident(list)operator(()keyword(new) ident(GlobFilenameFilter)operator(()string<delimiter(')content(*.c)delimiter(')>(\)\))
+ident(filelist) operator(=) ident(dir)operator(.)ident(listFiles)operator(()keyword(new) ident(GlobFilenameFilter)operator(()string<delimiter(')content(*.h)delimiter(')>(\)) keyword(as) pre_type(FilenameFilter)(\))
+
+comment(// Built-in matching using regex's)
+ident(files) operator(=) type([])
+keyword(new) pre_type(File)operator(()ident(basedir)(\))operator(.)ident(eachFileMatch)operator(()operator(~)regexp<delimiter(/)content(\\.)content([ch])content($)delimiter(/)>(\))operator({) ident(f)operator(->)
+ keyword(if) operator(()ident(f)operator(.)ident(isFile)operator(()(\)\)) ident(files) operator(+=) ident(f)
+(})
+
+comment(// Using Ant's FileScanner (supports arbitrary nested levels using **\))
+comment(// For more details about Ant FileSets, see here:)
+comment(// http://ant.apache.org/manual/CoreTypes/fileset.html)
+ident(scanner) operator(=) keyword(new) ident(AntBuilder)operator(()(\))operator(.)ident(fileScanner) operator({)
+ ident(fileset)operator(()key(dir)operator(:)ident(basedir)(\)) operator({)
+ ident(include)operator(()key(name)operator(:)string<delimiter(')content(**/pleac*.groovy)delimiter(')>(\))
+ ident(include)operator(()key(name)operator(:)string<delimiter(')content(Slowcat.*y)delimiter(')>(\))
+ ident(exclude)operator(()key(name)operator(:)string<delimiter(')content(**/pleac??.groovy)delimiter(')>(\)) comment(// chaps 10 and above)
+ ident(exclude)operator(()key(name)operator(:)string<delimiter(')content(**/*Test*)delimiter(')>operator(,) key(unless)operator(:)string<delimiter(')content(testMode)delimiter(')>(\))
+ (})
+(})
+keyword(for) operator(()ident(f) keyword(in) ident(scanner)(\)) operator({)
+ ident(println) string<delimiter(")content(Found file )inline<inline_delimiter($)ident(f)>delimiter(")>
+(})
+
+comment(// find and sort directories with numeric names)
+ident(candidateFiles) operator(=) keyword(new) pre_type(File)operator(()ident(basedir)(\))operator(.)ident(listFiles)operator(()(\))
+ident(allDigits) operator(=) operator({) local_variable(it)operator(.)ident(name) operator(=~) regexp<delimiter(/)content(^)char(\\d)content(+)content($)delimiter(/)> (})
+ident(isDir) operator(=) operator({) local_variable(it)operator(.)ident(isDirectory)operator(()(\)) (})
+ident(dirs) operator(=) ident(candidateFiles)operator(.)ident(findAll)operator(()ident(isDir)(\))operator(.)ident(findAll)operator(()ident(allDigits)(\))operator(*.)ident(canonicalPath)operator(.)ident(sort)operator(()(\))
+ident(println) ident(dirs)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_9.7)
+comment(//----------------------------------------------------------------------------------)
+comment(// find all files recursively)
+ident(dir) operator(=) keyword(new) pre_type(File)operator(()ident(basedir)(\))
+ident(files) operator(=) type([])
+ident(dir)operator(.)ident(eachFileRecurse)operator({) ident(files) operator(+=) local_variable(it) (})
+
+comment(// find total size)
+ident(sum) operator(=) ident(files)operator(.)ident(sum)operator({) local_variable(it)operator(.)ident(size)operator(()(\)) (})
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(basedir)>content( contains )inline<inline_delimiter($)ident(sum)>content( bytes)delimiter(")>
+comment(// => Pleac/src contains 365676 bytes)
+
+comment(// find biggest)
+ident(biggest) operator(=) ident(files)operator(.)ident(max)operator({) local_variable(it)operator(.)ident(size)operator(()(\)) (})
+ident(println) string<delimiter(")content(Biggest file is )inline<inline_delimiter($)ident(biggest)>content(.name with )inline<inline_delimiter(${)ident(biggest.size(\))inline_delimiter(})>content( bytes)delimiter(")>
+comment(// => Biggest file is pleac6.groovy with 42415 bytes)
+
+comment(// find most recently modified)
+ident(youngest) operator(=) ident(files)operator(.)ident(max)operator({) local_variable(it)operator(.)ident(lastModified)operator(()(\)) (})
+ident(println) string<delimiter(")content(Most recently modified is )inline<inline_delimiter($)ident(youngest)>content(.name, changed )inline<inline_delimiter(${)ident(new Date(youngest.lastModified(\)\))inline_delimiter(})>delimiter(")>
+comment(// => Most recently modified is pleac9.groovy, changed Tue Jan 09 07:35:39 EST 2007)
+
+comment(// find all directories)
+ident(dir)operator(.)ident(eachDir)operator({) ident(println) string<delimiter(')content(Found: )delimiter(')> operator(+) local_variable(it)operator(.)ident(name)(})
+
+comment(// find all directories recursively)
+ident(dir)operator(.)ident(eachFileRecurse)operator({) ident(f) operator(->) keyword(if) operator(()ident(f)operator(.)ident(isDirectory)operator(()(\)\)) ident(println) string<delimiter(')content(Found: )delimiter(')> operator(+) ident(f)operator(.)ident(canonicalPath)(})
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_9.8)
+comment(//----------------------------------------------------------------------------------)
+ident(base) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(path_to_somewhere_to_delete)delimiter(')>(\))
+
+comment(// delete using Jakarta Apache Commons IO)
+ident(FileUtils)operator(.)ident(deleteDirectory)operator(()ident(base)(\))
+
+comment(// delete using Ant, for various options see:)
+comment(// http://ant.apache.org/manual/CoreTasks/delete.html)
+ident(ant) operator(=) keyword(new) ident(AntBuilder)operator(()(\))
+ident(ant)operator(.)ident(delete)operator(()key(dir)operator(:) ident(base)(\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_9.9)
+comment(//----------------------------------------------------------------------------------)
+ident(names) operator(=) operator([)string<delimiter(')content(Pleac/src/abc.java)delimiter(')>operator(,) string<delimiter(')content(Pleac/src/def.groovy)delimiter(')>(])
+ident(names)operator(.)ident(each)operator({) ident(name) operator(->) keyword(new) pre_type(File)operator(()ident(name)(\))operator(.)ident(renameTo)operator(()keyword(new) pre_type(File)operator(()ident(name) operator(+) string<delimiter(')content(.bak)delimiter(')>(\)\)) (})
+
+comment(// The Groovy way of doing rename using an expr would be to use a closure)
+comment(// for the expr:)
+comment(// groovySimpleRenameScript:)
+doctype(#!/usr/bin/groovy)
+comment(// usage rename closure_expr filenames)
+ident(op) operator(=) ident(args)operator([)integer(0)(])
+ident(println) ident(op)
+ident(files) operator(=) ident(args)operator([)integer(1)operator(..)operator(-)integer(1)(])
+ident(shell) operator(=) keyword(new) ident(GroovyShell)operator(()ident(binding)(\))
+ident(files)operator(.)ident(each)operator({) ident(f) operator(->)
+ ident(newname) operator(=) ident(shell)operator(.)ident(evaluate)operator(()string<delimiter(")inline<inline_delimiter($)ident(op)>content((')inline<inline_delimiter($)ident(f)>content('\))delimiter(")>(\))
+ keyword(new) pre_type(File)operator(()ident(f)(\))operator(.)ident(renameTo)operator(()keyword(new) pre_type(File)operator(()ident(newname)(\)\))
+(})
+
+comment(// this would allow processing such as:)
+comment(//% rename "{n -> 'FILE_' + n.toUpperCase(\)}" files)
+comment(// with param pleac9.groovy => FILE_PLEAC9.GROOVY)
+comment(//% rename "{n -> n.replaceAll(/9/,'nine'\) }" files)
+comment(// with param pleac9.groovy => pleacnine.groovy)
+comment(// The script could also be modified to take the list of)
+comment(// files from stdin if no args were present (not shown\).)
+
+comment(// The above lets you type any Groovy code, but instead you might)
+comment(// decide to provide the user with some DSL-like additions, e.g.)
+comment(// adding the following lines into the script:)
+ident(sep) operator(=) pre_type(File)operator(.)ident(separator)
+ident(ext) operator(=) operator({) string<delimiter(')content(.)delimiter(')> operator(+) local_variable(it)operator(.)ident(tokenize)operator(()string<delimiter(')content(.)delimiter(')>(\))operator([)operator(-)integer(1)(]) (})
+ident(base) operator(=) operator({) keyword(new) pre_type(File)operator(()local_variable(it)(\))operator(.)ident(name) operator(-) ident(ext)operator(()local_variable(it)(\)) (})
+ident(parent) operator(=) operator({) keyword(new) pre_type(File)operator(()local_variable(it)(\))operator(.)ident(parent) (})
+ident(lastModified) operator(=) operator({) keyword(new) pre_type(Date)operator(()keyword(new) pre_type(File)operator(()local_variable(it)(\))operator(.)ident(lastModified)operator(()(\)\)) (})
+comment(// would then allow the following more succinct expressions:)
+comment(//% rename "{ n -> parent(n\) + sep + base(n\).reverse(\) + ext(n\) }" files)
+comment(// with param Pleac/src/pleac9.groovy => Pleac\\src\\9caelp.groovy)
+comment(//% rename "{ n -> base(n\) + '_' + lastModified(n\).year + ext(n\) }" files)
+comment(// with param pleac9.groovy => pleac9_07.groovy)
+
+comment(// As a different alternative, you could hook into Ant's mapper mechanism.)
+comment(// You wouldn't normally type in this from the command-line but it could)
+comment(// be part of a script, here is an example (excludes the actual rename part\))
+ident(ant) operator(=) keyword(new) ident(AntBuilder)operator(()(\))
+ident(ant)operator(.)ident(pathconvert)operator(()key(property)operator(:)string<delimiter(')content(result)delimiter(')>operator(,)key(targetos)operator(:)string<delimiter(')content(windows)delimiter(')>(\))operator({)
+ ident(path)operator(()(\))operator({) ident(fileset)operator(()key(dir)operator(:)string<delimiter(')content(Pleac/src)delimiter(')>operator(,) key(includes)operator(:)string<delimiter(')content(pleac?.groovy)delimiter(')>(\)) (})
+ ident(compositemapper)operator({)
+ ident(globmapper)operator(()key(from)operator(:)string<delimiter(')content(*1.groovy)delimiter(')>operator(,) key(to)operator(:)string<delimiter(')content(*1.groovy.bak)delimiter(')>(\))
+ ident(regexpmapper)operator(()key(from)operator(:)regexp<delimiter(/)content(^(.*C2\))content(\\.)content((.*\))content($)delimiter(/)>operator(,) key(to)operator(:)regexp<delimiter(/)char(\\1)content(_beta.)char(\\2)delimiter(/)>operator(,) key(casesensitive)operator(:)string<delimiter(')content(no)delimiter(')>(\))
+ ident(chainedmapper)operator({)
+ ident(packagemapper)operator(()key(from)operator(:)string<delimiter(')content(*pleac3.groovy)delimiter(')>operator(,) key(to)operator(:)string<delimiter(')content(*3.xml)delimiter(')>(\))
+ ident(filtermapper)operator(()(\))operator({) ident(replacestring)operator(()key(from)operator(:)string<delimiter(')content(C:.)delimiter(')>operator(,) key(to)operator(:)string<delimiter(')delimiter(')>(\)) (})
+ (})
+ ident(chainedmapper)operator({)
+ ident(regexpmapper)operator(()key(from)operator(:)regexp<delimiter(/)content(^(.*\)4)content(\\.)content((.*\))content($)delimiter(/)>operator(,) key(to)operator(:)regexp<delimiter(/)char(\\1)content(_4.)char(\\2)delimiter(/)>(\))
+ ident(flattenmapper)operator(()(\))
+ ident(filtermapper)operator(()(\))operator({) ident(replacestring)operator(()key(from)operator(:)string<delimiter(')content(4)delimiter(')>operator(,) key(to)operator(:)string<delimiter(')content(four)delimiter(')>(\)) (})
+ (})
+ (})
+(})
+ident(println) ident(ant)operator(.)ident(antProject)operator(.)ident(getProperty)operator(()string<delimiter(')content(result)delimiter(')>(\))operator(.)ident(replaceAll)operator(()string<delimiter(')content(;)delimiter(')>operator(,)string<delimiter(')content(\\n)delimiter(')>(\))
+comment(// =>)
+comment(// C:\\Projects\\GroovyExamples\\Pleac\\src\\pleac1.groovy.bak)
+comment(// C:\\Projects\\GroovyExamples\\Pleac\\src\\pleac2_beta.groovy)
+comment(// Projects.GroovyExamples.Pleac.src.3.xml)
+comment(// pleac_four.groovy)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_9.10)
+comment(//----------------------------------------------------------------------------------)
+comment(// Splitting a Filename into Its Component Parts)
+ident(path) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/src/pleac9.groovy)delimiter(')>(\))
+keyword(assert) ident(path)operator(.)ident(parent) operator(==) string<delimiter(')content(Pleac)delimiter(')> operator(+) pre_type(File)operator(.)ident(separator) operator(+) string<delimiter(')content(src)delimiter(')>
+keyword(assert) ident(path)operator(.)ident(name) operator(==) string<delimiter(')content(pleac9.groovy)delimiter(')>
+ident(ext) operator(=) ident(path)operator(.)ident(name)operator(.)ident(tokenize)operator(()string<delimiter(')content(.)delimiter(')>(\))operator([)operator(-)integer(1)(])
+keyword(assert) ident(ext) operator(==) string<delimiter(')content(groovy)delimiter(')>
+
+comment(// No fileparse_set_fstype(\) equivalent in Groovy/Java. Java's File constructor)
+comment(// automatically performs such a parse and does so appropriately of the operating)
+comment(// system it is running on. In addition, 3rd party libraries allow platform)
+comment(// specific operations ot be performed. As an example, many Ant tasks are OS)
+comment(// aware, e.g. the pathconvert task (callable from an AntBuilder instance\) has)
+comment(// a 'targetos' parameter which can be one of 'unix', 'windows', 'netware',)
+comment(// 'tandem' or 'os/2'.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_9.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// Given the previous discussion regarding the lack of support for symlinks)
+comment(// in Java's File class without exec'ing to the operating system or doing)
+comment(// a JNI call (at least until JSR 203 arrives\), I have modified this example)
+comment(// to perform an actual replica forest of actual file copies rather than)
+comment(// a shadow forest full of symlinks pointing back at the real files.)
+comment(// Use Apache Jakarta Commons IO)
+ident(srcdir) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(Pleac/src)delimiter(')>(\)) comment(// path to src)
+ident(destdir) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(C:/temp)delimiter(')>(\)) comment(// path to dest)
+ident(preserveFileStamps) operator(=) keyword(true)
+ident(FileUtils)operator(.)ident(copyDirectory)operator(()ident(srcdir)operator(,) ident(destdir)operator(,) ident(preserveFileStamps)(\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_9.12)
+comment(//----------------------------------------------------------------------------------)
+doctype(#!/usr/bin/groovy)
+comment(// lst - list sorted directory contents (depth first\))
+comment(// Given the previous discussion around Java's more limited Date)
+comment(// information available via the File class, this will be a reduced)
+comment(// functionality version of ls)
+ident(LONG_OPTION) operator(=) string<delimiter(')content(l)delimiter(')>
+ident(REVERSE_OPTION) operator(=) string<delimiter(')content(r)delimiter(')>
+ident(MODIFY_OPTION) operator(=) string<delimiter(')content(m)delimiter(')>
+ident(SIZE_OPTION) operator(=) string<delimiter(')content(s)delimiter(')>
+ident(HELP_OPTION) operator(=) string<delimiter(')content(help)delimiter(')>
+
+ident(op) operator(=) keyword(new) ident(joptsimple)operator(.)ident(OptionParser)operator(()(\))
+ident(op)operator(.)ident(accepts)operator(() ident(LONG_OPTION)operator(,) string<delimiter(')content(long listing)delimiter(')> (\))
+ident(op)operator(.)ident(accepts)operator(() ident(REVERSE_OPTION)operator(,) string<delimiter(')content(reverse listing)delimiter(')> (\))
+ident(op)operator(.)ident(accepts)operator(() ident(MODIFY_OPTION)operator(,) string<delimiter(')content(sort based on modification time)delimiter(')> (\))
+ident(op)operator(.)ident(accepts)operator(() ident(SIZE_OPTION)operator(,) string<delimiter(')content(sort based on size)delimiter(')> (\))
+ident(op)operator(.)ident(accepts)operator(() ident(HELP_OPTION)operator(,) string<delimiter(')content(display this message)delimiter(')> (\))
+
+ident(options) operator(=) ident(op)operator(.)ident(parse)operator(()ident(args)(\))
+keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(() ident(HELP_OPTION) (\)\)) operator({)
+ ident(op)operator(.)ident(printHelpOn)operator(() pre_type(System)operator(.)ident(out) (\))
+(}) keyword(else) operator({)
+ ident(sort) operator(=) operator({)(})
+ ident(params) operator(=) ident(options)operator(.)ident(nonOptionArguments)operator(()(\))
+ ident(longFormat) operator(=) ident(options)operator(.)ident(wasDetected)operator(() ident(LONG_OPTION) (\))
+ ident(reversed) operator(=) ident(options)operator(.)ident(wasDetected)operator(() ident(REVERSE_OPTION) (\))
+ keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(() ident(SIZE_OPTION) (\)\)) operator({)
+ ident(sort) operator(=) operator({)ident(a)operator(,)ident(b) operator(->) ident(a)operator(.)ident(size)operator(()(\))operator(<=)operator(>)ident(b)operator(.)ident(size)operator(()(\)})
+ (}) keyword(else) keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(() ident(MODIFY_OPTION) (\)\)) operator({)
+ ident(sort) operator(=) operator({)ident(a)operator(,)ident(b) operator(->) ident(a)operator(.)ident(lastModified)operator(()(\))operator(<=)operator(>)ident(b)operator(.)ident(lastModified)operator(()(\)})
+ (})
+ ident(displayFiles)operator(()ident(params)operator(,) ident(longFormat)operator(,) ident(reversed)operator(,) ident(sort)(\))
+(})
+
+keyword(def) method(displayFiles)operator(()ident(params)operator(,) ident(longFormat)operator(,) ident(reversed)operator(,) ident(sort)(\)) operator({)
+ ident(files) operator(=) type([])
+ ident(params)operator(.)ident(each)operator({) ident(name) operator(->) keyword(new) pre_type(File)operator(()ident(name)(\))operator(.)ident(eachFileRecurse)operator({) ident(files) operator(+=) local_variable(it) (}) (})
+ ident(files)operator(.)ident(sort)operator(()ident(sort)(\))
+ keyword(if) operator(()ident(reversed)(\)) ident(files) operator(=) ident(files)operator(.)ident(reverse)operator(()(\))
+ ident(files)operator(.)ident(each) operator({) ident(file) operator(->)
+ keyword(if) operator(()ident(longFormat)(\)) operator({)
+ ident(print) operator(()ident(file)operator(.)ident(directory) operator(?) string<delimiter(')content(d)delimiter(')> operator(:) string<delimiter(')content(-)delimiter(')> (\))
+ ident(print) operator(()ident(file)operator(.)ident(canRead)operator(()(\)) operator(?) string<delimiter(')content(r)delimiter(')> operator(:) string<delimiter(')content(-)delimiter(')> (\))
+ ident(print) operator(()ident(file)operator(.)ident(canWrite)operator(()(\)) operator(?) string<delimiter(')content(w )delimiter(')> operator(:) string<delimiter(')content(- )delimiter(')> (\))
+ comment(//print (file.canExecute(\) ? 'x' : '-' \) // Java 6)
+ ident(print) ident(file)operator(.)ident(size)operator(()(\))operator(.)ident(toString)operator(()(\))operator(.)ident(padLeft)operator(()integer(12)(\)) operator(+) string<delimiter(')content( )delimiter(')>
+ ident(print) keyword(new) pre_type(Date)operator(()ident(file)operator(.)ident(lastModified)operator(()(\)\))operator(.)ident(toString)operator(()(\))operator(.)ident(padRight)operator(()integer(22)(\))
+ ident(println) string<delimiter(')content( )delimiter(')> operator(+) ident(file)
+ (}) keyword(else) operator({)
+ ident(println) ident(file)
+ (})
+ (})
+(})
+
+comment(// =>)
+comment(// % lst -help)
+comment(// Option Description)
+comment(// ------ -------------------------------)
+comment(// --help display this message)
+comment(// -l long listing)
+comment(// -m sort based on modification time)
+comment(// -r reverse listing)
+comment(// -s sort based on size)
+comment(//)
+comment(// % lst -l -m Pleac/src Pleac/lib)
+comment(// ...)
+comment(// drw 0 Mon Jan 08 22:33:00 EST 2007 Pleac\\lib\\.svn)
+comment(// -rw 18988 Mon Jan 08 22:33:41 EST 2007 Pleac\\src\\pleac9.groovy)
+comment(// -rw 2159 Mon Jan 08 23:15:41 EST 2007 Pleac\\src\\lst.groovy)
+comment(//)
+comment(// % -l -s -r Pleac/src Pleac/lib)
+comment(// -rw 1034049 Sun Jan 07 19:24:41 EST 2007 Pleac\\lib\\ant.jar)
+comment(// -r- 1034049 Sun Jan 07 19:40:27 EST 2007 Pleac\\lib\\.svn\\text-base\\ant.jar.svn-base)
+comment(// -rw 421008 Thu Jun 02 15:15:34 EST 2005 Pleac\\lib\\ant-nodeps.jar)
+comment(// -rw 294436 Sat Jan 06 21:19:58 EST 2007 Pleac\\lib\\geronimo-javamail_1.3.1_mail-1.0.jar)
+comment(// ...)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_10.0)
+comment(//----------------------------------------------------------------------------------)
+keyword(def) method(hello)operator(()(\)) operator({)
+ ident(greeted) operator(+=) integer(1)
+ ident(println) string<delimiter(")content(hi there!)delimiter(")>
+(})
+
+comment(// We need to initialize greeted before it can be used, because "+=" assumes predefinition)
+ident(greeted) operator(=) integer(0)
+ident(hello)operator(()(\))
+ident(println) ident(greeted)
+comment(// =>)
+comment(// hi there)
+comment(// 1)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.1)
+comment(//----------------------------------------------------------------------------------)
+comment(// basic method calling examples)
+comment(// In Groovy, parameters are named anyway)
+keyword(def) method(hypotenuse)operator(()ident(side1)operator(,) ident(side2)(\)) operator({)
+ pre_type(Math)operator(.)ident(sqrt)operator(()ident(side1)operator(**)integer(2) operator(+) ident(side2)operator(**)integer(2)(\)) comment(// sqrt in Math package)
+(})
+ident(diag) operator(=) ident(hypotenuse)operator(()integer(3)operator(,) integer(4)(\))
+keyword(assert) ident(diag) operator(==) integer(5)
+
+comment(// the star operator will magically convert an Array into a "tuple")
+ident(a) operator(=) operator([)integer(5)operator(,) integer(12)(])
+keyword(assert) ident(hypotenuse)operator(()operator(*)ident(a)(\)) operator(==) integer(13)
+
+comment(// both = men + women)
+
+comment(// In Groovy, all objects are references, so the same problem arises.)
+comment(// Typically we just return a new object. Especially for immutable objects)
+comment(// this style of processing is very common.)
+ident(nums) operator(=) operator([)float(1.4)operator(,) float(3.5)operator(,) float(6.7)(])
+keyword(def) method(toInteger)operator(()ident(n)(\)) operator({)
+ ident(n)operator(.)ident(collect) operator({) ident(v) operator(->) ident(v)operator(.)ident(toInteger)operator(()(\)) (})
+(})
+keyword(assert) ident(toInteger)operator(()ident(nums)(\)) operator(==) operator([)integer(1)operator(,) integer(3)operator(,) integer(6)(])
+
+ident(orignums) operator(=) operator([)float(1.4)operator(,) float(3.5)operator(,) float(6.7)(])
+keyword(def) method(truncMe)operator(()ident(n)(\)) operator({)
+ operator(()integer(0)operator(..<)ident(n)operator(.)ident(size)operator(()(\)\))operator(.)ident(each)operator({) ident(idx) operator(->) ident(n)operator([)ident(idx)(]) operator(=) ident(n)operator([)ident(idx)(])operator(.)ident(toInteger)operator(()(\)) (})
+(})
+ident(truncMe)operator(()ident(orignums)(\))
+keyword(assert) ident(orignums) operator(==) operator([)integer(1)operator(,) integer(3)operator(,) integer(6)(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// variable scope examples)
+keyword(def) method(somefunc)operator(()(\)) operator({)
+ keyword(def) ident(variableInMethod) comment(// private is default in a method)
+(})
+
+keyword(def) ident(name) comment(// private is default for variable in a script)
+
+ident(bindingVar) operator(=) integer(10) comment(// this will be in the binding (sort of global\))
+ident(globalArray) operator(=) type([])
+
+comment(// In Groovy, run_check can't access a, b, or c until they are)
+comment(// explicitely defined global (using leading $\), even if they are)
+comment(// both defined in the same scope)
+
+keyword(def) method(checkAccess)operator(()ident(x)(\)) operator({)
+ keyword(def) ident(y) operator(=) integer(200)
+ keyword(return) ident(x) operator(+) ident(y) operator(+) ident(bindingVar) comment(// access private, param, global)
+(})
+keyword(assert) ident(checkAccess)operator(()integer(7)(\)) operator(==) integer(217)
+
+keyword(def) method(saveArray)operator(()ident(ary)(\)) operator({)
+ ident(globalArray) operator(<)operator(<) string<delimiter(')content(internal)delimiter(')>
+ ident(globalArray) operator(+=) ident(ary)
+(})
+
+ident(saveArray)operator(()operator([)string<delimiter(')content(important)delimiter(')>(]\))
+keyword(assert) ident(globalArray) operator(==) operator([)string<delimiter(")content(internal)delimiter(")>operator(,) string<delimiter(")content(important)delimiter(")>(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// you want a private persistent variable within a script method)
+
+comment(// you could use a helper class for this)
+type(class) class(CounterHelper) operator({)
+ directive(private) directive(static) ident(counter) operator(=) integer(0)
+ keyword(def) directive(static) method(next)operator(()(\)) operator({) operator(++)ident(counter) (})
+(})
+keyword(def) method(greeting)operator(()ident(s)(\)) operator({)
+ keyword(def) ident(n) operator(=) ident(CounterHelper)operator(.)ident(next)operator(()(\))
+ ident(println) string<delimiter(")content(Hello )inline<inline_delimiter($)ident(s)>content( (I have been called )inline<inline_delimiter($)ident(n)>content( times\))delimiter(")>
+(})
+ident(greeting)operator(()string<delimiter(')content(tom)delimiter(')>(\))
+ident(greeting)operator(()string<delimiter(')content(dick)delimiter(')>(\))
+ident(greeting)operator(()string<delimiter(')content(harry)delimiter(')>(\))
+comment(// =>)
+comment(// Hello tom (I have been called 1 times\))
+comment(// Hello dick (I have been called 2 times\))
+comment(// Hello harry (I have been called 3 times\))
+
+comment(// you could make it more fancy by having separate keys,)
+comment(// using synchronisation, singleton pattern, ThreadLocal, ...)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_10.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// Determining Current Method Name)
+comment(// Getting class, package and static info is easy. Method info is just a little work.)
+comment(// From Java we can use:)
+comment(// new Exception(\).stackTrace[0].methodName)
+comment(// or for Java 5 and above (saves relatively expensive exception creation\))
+comment(// Thread.currentThread(\).stackTrace[3].methodName)
+comment(// But these give the Java method name. Groovy wraps its own runtime)
+comment(// system over the top. It's still a Java method, just a little bit further up the)
+comment(// stack from where we might expect. Getting the Groovy method name can be done in)
+comment(// an implementation specific way (subject to change as the language evolves\):)
+keyword(def) method(myMethod)operator(()(\)) operator({)
+ ident(names) operator(=) keyword(new) pre_type(Exception)operator(()(\))operator(.)ident(stackTrace)operator(*.)ident(methodName)
+ ident(println) ident(groovyUnwrap)operator(()ident(names)(\))
+(})
+keyword(def) method(myMethod2)operator(()(\)) operator({)
+ ident(names) operator(=) pre_type(Thread)operator(.)ident(currentThread)operator(()(\))operator(.)ident(stackTrace)operator(*.)ident(methodName)
+ ident(names) operator(=) ident(names)operator([)integer(3)operator(..<)ident(names)operator(.)ident(size)operator(()(\)]) comment(// skip call to dumpThread)
+ ident(println) ident(groovyUnwrap)operator(()ident(names)(\))
+(})
+keyword(def) method(groovyUnwrap)operator(()ident(names)(\)) operator({) ident(names)operator([)ident(names)operator(.)ident(indexOf)operator(()string<delimiter(')content(invoke0)delimiter(')>(\))operator(-)integer(1)(]) (})
+ident(myMethod)operator(()(\)) comment(// => myMethod)
+ident(myMethod2)operator(()(\)) comment(// => myMethod2)
+
+comment(// Discussion: If what you really wanted was a tracing mechanism, you could overrie)
+comment(// invokeMethod and print out method names before calling the original method. Or)
+comment(// you could use one of the Aspect-Oriented Programming packages for Java.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// Passing Arrays and Hashes by Reference)
+comment(// In Groovy, every value is a reference to an object, thus there is)
+comment(// no such problem, just call: arrayDiff(array1, array2\))
+
+comment(// pairwise add (altered so it doesn't require equal sizes\))
+keyword(def) method(pairWiseAdd)operator(()ident(a1)operator(,) ident(a2)(\)) operator({)
+ ident(s1) operator(=) ident(a1)operator(.)ident(size)operator(()(\))operator(;) ident(s2) operator(=) ident(a2)operator(.)ident(size)operator(()(\))
+ operator(()integer(0)operator(..<)operator([)ident(s1)operator(,)ident(s2)(])operator(.)ident(max)operator(()(\)\))operator(.)ident(collect)operator({)
+ local_variable(it) operator(>) ident(s1)operator(-)integer(1) operator(?) ident(a2)operator([)local_variable(it)(]) operator(:) operator(()local_variable(it) operator(>) ident(s2)operator(-)integer(1) operator(?) ident(a1)operator([)local_variable(it)(]) operator(:) ident(a1)operator([)local_variable(it)(]) operator(+) ident(a2)operator([)local_variable(it)(]\))
+ (})
+(})
+ident(a) operator(=) operator([)integer(1)operator(,) integer(2)(])
+ident(b) operator(=) operator([)integer(5)operator(,) integer(8)(])
+keyword(assert) ident(pairWiseAdd)operator(()ident(a)operator(,) ident(b)(\)) operator(==) operator([)integer(6)operator(,) integer(10)(])
+
+comment(// also works for unequal sizes)
+ident(b) operator(=) operator([)integer(5)operator(,) integer(8)operator(,) operator(-)integer(1)(])
+keyword(assert) ident(pairWiseAdd)operator(()ident(a)operator(,) ident(b)(\)) operator(==) operator([)integer(6)operator(,) integer(10)operator(,) operator(-)integer(1)(])
+ident(b) operator(=) operator([)integer(5)(])
+keyword(assert) ident(pairWiseAdd)operator(()ident(a)operator(,) ident(b)(\)) operator(==) operator([)integer(6)operator(,) integer(2)(])
+
+comment(// We could check if both arguments were of a particular type, e.g.)
+comment(// (a1 instanceof List\) or (a2.class.isArray(\)\) but duck typing allows)
+comment(// it to work on other things as well, so while wouldn't normally do this)
+comment(// you do need to be a little careful when calling the method, e.g.)
+comment(// here we call it with two maps of strings and get back strings)
+comment(// the important thing here was that the arguments were indexed)
+comment(// 0..size-1 and that the items supported the '+' operator (as String does\))
+ident(a) operator(=) operator([)integer(0)operator(:)string<delimiter(')content(Green )delimiter(')>operator(,) integer(1)operator(:)string<delimiter(')content(Grey )delimiter(')>(])
+ident(b) operator(=) operator([)integer(0)operator(:)string<delimiter(')content(Frog)delimiter(')>operator(,) integer(1)operator(:)string<delimiter(')content(Elephant)delimiter(')>operator(,) integer(2)operator(:)string<delimiter(')content(Dog)delimiter(')>(])
+keyword(assert) ident(pairWiseAdd)operator(()ident(a)operator(,) ident(b)(\)) operator(==) operator([)string<delimiter(")content(Green Frog)delimiter(")>operator(,) string<delimiter(")content(Grey Elephant)delimiter(")>operator(,) string<delimiter(")content(Dog)delimiter(")>(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// Detecting Return Context)
+comment(// There is no exact equivalent of return context in Groovy but)
+comment(// you can behave differently when called under different circumstances)
+keyword(def) method(addValueOrSize)operator(()ident(a1)operator(,) ident(a2)(\)) operator({)
+ ident(b1) operator(=) operator(()ident(a1) keyword(instanceof) pre_type(Number)(\)) operator(?) ident(a1) operator(:) ident(a1)operator(.)ident(size)operator(()(\))
+ ident(b2) operator(=) operator(()ident(a2) keyword(instanceof) pre_type(Number)(\)) operator(?) ident(a2) operator(:) ident(a2)operator(.)ident(size)operator(()(\))
+ ident(b1) operator(+) ident(b2)
+(})
+keyword(assert) operator(()ident(addValueOrSize)operator(()integer(10)operator(,) string<delimiter(')content(abcd)delimiter(')>(\)\)) operator(==) integer(14)
+keyword(assert) operator(()ident(addValueOrSize)operator(()integer(10)operator(,) operator([)integer(25)operator(,) integer(50)(]\)\)) operator(==) integer(12)
+keyword(assert) operator(()ident(addValueOrSize)operator(()string<delimiter(')content(abc)delimiter(')>operator(,) operator([)integer(25)operator(,) integer(50)(]\)\)) operator(==) integer(5)
+keyword(assert) operator(()ident(addValueOrSize)operator(()integer(25)operator(,) integer(50)(\)\)) operator(==) integer(75)
+
+comment(// Of course, a key feature of many OO languages including Groovy is)
+comment(// method overloading so that responding to dofferent parameters has)
+comment(// a formal way of being captured in code with typed methods, e.g.)
+type(class) class(MakeBiggerHelper) operator({)
+ keyword(def) method(triple)operator(()pre_type(List) ident(iList)(\)) operator({) ident(iList)operator(.)ident(collect)operator({) local_variable(it) operator(*) integer(3) (}) (})
+ keyword(def) method(triple)operator(()type(int) ident(i)(\)) operator({) ident(i) operator(*) integer(3) (})
+(})
+ident(mbh) operator(=) keyword(new) ident(MakeBiggerHelper)operator(()(\))
+keyword(assert) ident(mbh)operator(.)ident(triple)operator(()operator([)integer(4)operator(,) integer(5)(]\)) operator(==) operator([)integer(12)operator(,) integer(15)(])
+keyword(assert) ident(mbh)operator(.)ident(triple)operator(()integer(4)(\)) operator(==) integer(12)
+
+comment(// Of course with duck typing, we can rely on dynamic typing if we want)
+keyword(def) method(directTriple)operator(()ident(arg)(\)) operator({)
+ operator(()ident(arg) keyword(instanceof) pre_type(Number)(\)) operator(?) ident(arg) operator(*) integer(3) operator(:) ident(arg)operator(.)ident(collect)operator({) local_variable(it) operator(*) integer(3) (})
+(})
+keyword(assert) ident(directTriple)operator(()operator([)integer(4)operator(,) integer(5)(]\)) operator(==) operator([)integer(12)operator(,) integer(15)(])
+keyword(assert) ident(directTriple)operator(()integer(4)(\)) operator(==) integer(12)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.7)
+comment(//----------------------------------------------------------------------------------)
+comment(// Passing by Named Parameter)
+comment(// Groovy supports named params or positional arguments with optional)
+comment(// defaults to simplify method calling)
+
+comment(// named arguments work by using a map)
+keyword(def) method(thefunc)operator(()pre_type(Map) ident(args)(\)) operator({)
+ comment(// in this example, we just call the positional version)
+ ident(thefunc)operator(()ident(args)operator(.)ident(start)operator(,) ident(args)operator(.)ident(end)operator(,) ident(args)operator(.)ident(step)(\))
+(})
+
+comment(// positional arguments with defaults)
+keyword(def) method(thefunc)operator(()ident(start)operator(=)integer(0)operator(,) ident(end)operator(=)integer(30)operator(,) ident(step)operator(=)integer(10)(\)) operator({)
+ operator(()operator(()ident(start)operator(..)ident(end)(\))operator(.)ident(step)operator(()ident(step)(\)\))
+(})
+
+keyword(assert) ident(thefunc)operator(()(\)) operator(==) operator([)integer(0)operator(,) integer(10)operator(,) integer(20)operator(,) integer(30)(])
+keyword(assert) ident(thefunc)operator(()integer(15)(\)) operator(==) operator([)integer(15)operator(,) integer(25)(])
+keyword(assert) ident(thefunc)operator(()integer(0)operator(,)integer(40)(\)) operator(==) operator([)integer(0)operator(,) integer(10)operator(,) integer(20)operator(,) integer(30)operator(,) integer(40)(])
+keyword(assert) ident(thefunc)operator(()key(start)operator(:)integer(5)operator(,) key(end)operator(:)integer(20)operator(,) key(step)operator(:)integer(5)(\)) operator(==) operator([)integer(5)operator(,) integer(10)operator(,) integer(15)operator(,) integer(20)(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.8)
+comment(//----------------------------------------------------------------------------------)
+comment(// Skipping Selected Return Values)
+comment(// Groovy 1.0 doesn't support multiple return types, so you always use)
+comment(// a holder class, array or collection to return multiple values.)
+keyword(def) method(getSystemInfo)operator(()(\)) operator({)
+ keyword(def) ident(millis) operator(=) pre_type(System)operator(.)ident(currentTimeMillis)operator(()(\))
+ keyword(def) ident(freemem) operator(=) pre_type(Runtime)operator(.)ident(runtime)operator(.)ident(freeMemory)operator(()(\))
+ keyword(def) ident(version) operator(=) pre_type(System)operator(.)ident(getProperty)operator(()string<delimiter(')content(java.vm.version)delimiter(')>(\))
+ keyword(return) operator([)key(millis)operator(:)ident(millis)operator(,) key(freemem)operator(:)ident(freemem)operator(,) key(version)operator(:)ident(version)(])
+ comment(// if you are likely to want all the information use a list)
+ comment(// return [millis, freemem, version])
+ comment(// or dedicated holder class)
+ comment(// return new SystemInfo(millis, freemem, version\))
+(})
+ident(result) operator(=) ident(getSystemInfo)operator(()(\))
+ident(println) ident(result)operator(.)ident(version)
+comment(// => 1.5.0_08-b03)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.9)
+comment(//----------------------------------------------------------------------------------)
+comment(// Returning More Than One Array or Hash)
+comment(// As per 10.8, Groovy 1.0 doesn't support multiple return types but you)
+comment(// just use a holder class, array or collection. There are no limitations)
+comment(// on returning arbitrary nested values using this technique.)
+keyword(def) method(getInfo)operator(()(\)) operator({)
+ keyword(def) ident(system) operator(=) operator([)key(millis)operator(:)pre_type(System)operator(.)ident(currentTimeMillis)operator(()(\))operator(,)
+ key(version)operator(:)pre_type(System)operator(.)ident(getProperty)operator(()string<delimiter(')content(java.vm.version)delimiter(')>(\)])
+ keyword(def) ident(runtime) operator(=) operator([)key(freemem)operator(:)pre_type(Runtime)operator(.)ident(runtime)operator(.)ident(freeMemory)operator(()(\))operator(,)
+ key(maxmem)operator(:)pre_type(Runtime)operator(.)ident(runtime)operator(.)ident(maxMemory)operator(()(\)])
+ keyword(return) operator([)key(system)operator(:)ident(system)operator(,) key(runtime)operator(:)ident(runtime)(])
+(})
+ident(println) ident(info)operator(.)ident(runtime)operator(.)ident(maxmem) comment(// => 66650112 (info automatically calls getInfo(\) here\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.10)
+comment(//----------------------------------------------------------------------------------)
+comment(// Returning Failure)
+comment(// This is normally done in a heavy-weight way via Java Exceptions)
+comment(// (see 10.12\) or in a lightweight way by returning null)
+keyword(def) method(sizeMinusOne)operator(()ident(thing)(\)) operator({)
+ keyword(if) operator(()ident(thing) keyword(instanceof) pre_type(Number)(\)) keyword(return)
+ ident(thing)operator(.)ident(size)operator(()(\)) operator(-) integer(1)
+(})
+keyword(def) method(check)operator(()ident(thing)(\)) operator({)
+ ident(result) operator(=) ident(sizeMinusOne)operator(()ident(thing)(\))
+ ident(println) operator(()ident(result) operator(?) string<delimiter(")content(Worked with result: )inline<inline_delimiter($)ident(result)>delimiter(")> operator(:) string<delimiter(')content(Failed)delimiter(')>(\))
+(})
+ident(check)operator(()integer(4)(\))
+ident(check)operator(()operator([)integer(1)operator(,) integer(2)(]\))
+ident(check)operator(()string<delimiter(')content(abc)delimiter(')>(\))
+comment(// =>)
+comment(// Failed)
+comment(// Worked with result: 1)
+comment(// Worked with result: 2)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// Prototyping Functions: Not supported by Groovy but arguably)
+comment(// not important given other language features.)
+
+comment(// Omitting Parentheses Scenario: Groovy only lets you leave out)
+comment(// parentheses in simple cases. If you had two methods sum(a1,a2,a3\))
+comment(// and sum(a1,a2\), there would be no way to indicate that whether)
+comment(// 'sum sum 2, 3, 4, 5' meant sum(sum(2,3\),4,5\) or sum(sum(2,3,4\),5\).)
+comment(// You would have to include the parentheses. Groovy does much less)
+comment(// auto flattening than some other languages; it provides a *args)
+comment(// operator, varargs style optional params and supports method)
+comment(// overloading and ducktyping. Perhaps these other features mean)
+comment(// that this scenario is always easy to avoid.)
+keyword(def) method(sum)operator(()ident(a)operator(,)ident(b)operator(,)ident(c)(\))operator({) ident(a)operator(+)ident(b)operator(+)ident(c)operator(*)integer(2) (})
+keyword(def) method(sum)operator(()ident(a)operator(,)ident(b)(\))operator({) ident(a)operator(+)ident(b) (})
+comment(// sum sum 1,2,4,5)
+comment(// => compilation error)
+ident(sum) ident(sum)operator(()integer(1)operator(,)integer(2)(\))operator(,)integer(4)operator(,)integer(5)
+ident(sum) ident(sum)operator(()integer(1)operator(,)integer(2)operator(,)integer(4)(\))operator(,)integer(5)
+comment(// these work but if you try to do anything fancy you will run into trouble;)
+comment(// your best bet is to actually include all the parentheses:)
+ident(println) ident(sum)operator(()ident(sum)operator(()integer(1)operator(,)integer(2)(\))operator(,)integer(4)operator(,)integer(5)(\)) comment(// => 17)
+ident(println) ident(sum)operator(()ident(sum)operator(()integer(1)operator(,)integer(2)operator(,)integer(4)(\))operator(,)integer(5)(\)) comment(// => 16)
+
+comment(// Mimicking built-ins scenario: this is a mechanism to turn-off)
+comment(// auto flattening, Groovy only does flattening in restricted circumstances.)
+comment(// func(array, 1, 2, 3\) is never coerced into a single list but varargs)
+comment(// and optional args can be used instead)
+keyword(def) method(push)operator(()ident(list)operator(,) pre_type(Object)type([]) ident(optionals)(\)) operator({)
+ ident(optionals)operator(.)ident(each)operator({) ident(list)operator(.)ident(add)operator(()local_variable(it)(\)) (})
+(})
+ident(items) operator(=) operator([)integer(1)operator(,)integer(2)(])
+ident(newItems) operator(=) operator([)integer(7)operator(,) integer(8)operator(,) integer(9)(])
+ident(push) ident(items)operator(,) integer(3)operator(,) integer(4)
+ident(push) ident(items)operator(,) integer(6)
+ident(push) operator(()ident(items)operator(,) operator(*)ident(newItems)(\)) comment(// brackets currently required, *=flattening)
+ comment(// without *: items = [1, 2, 3, 4, 6, [7, 8, 9]])
+keyword(assert) ident(items) operator(==) operator([)integer(1)operator(,) integer(2)operator(,) integer(3)operator(,) integer(4)operator(,) integer(6)operator(,) integer(7)operator(,) integer(8)operator(,) integer(9)(])
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.12)
+comment(//----------------------------------------------------------------------------------)
+comment(// Handling Exceptions)
+comment(// Same story as in Java but Groovy has some nice Checked -> Unchecked)
+comment(// magic behind the scenes (Java folk will know what this means\))
+comment(// When writing methods:)
+comment(// throw exception to raise it)
+comment(// When calling methods:)
+comment(// try ... catch ... finally surrounds processing logic)
+keyword(def) method(getSizeMostOfTheTime)operator(()ident(s)(\)) operator({)
+ keyword(if) operator(()ident(s) operator(=~) string<delimiter(')content(Full Moon)delimiter(')>(\)) keyword(throw) keyword(new) pre_type(RuntimeException)operator(()string<delimiter(')content(The world is ending)delimiter(')>(\))
+ ident(s)operator(.)ident(size)operator(()(\))
+(})
+keyword(try) operator({)
+ ident(println) string<delimiter(')content(Size is: )delimiter(')> operator(+) ident(getSizeMostOfTheTime)operator(()string<delimiter(')content(The quick brown fox)delimiter(')>(\))
+ ident(println) string<delimiter(')content(Size is: )delimiter(')> operator(+) ident(getSizeMostOfTheTime)operator(()string<delimiter(')content(Beware the Full Moon)delimiter(')>(\))
+(}) keyword(catch) operator(()pre_type(Exception) ident(ex)(\)) operator({)
+ ident(println) string<delimiter(")content(Error was: )inline<inline_delimiter($)ident(ex)>content(.message)delimiter(")>
+(}) keyword(finally) operator({)
+ ident(println) string<delimiter(')content(Doing common cleanup)delimiter(')>
+(})
+comment(// =>)
+comment(// Size is: 19)
+comment(// Error was: The world is ending)
+comment(// Doing common cleanup)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// Saving Global Values)
+comment(// We can just save the value and restore it later:)
+keyword(def) method(printAge)operator(()(\)) operator({) ident(println) string<delimiter(")content(Age is )inline<inline_delimiter($)ident(age)>delimiter(")> (})
+
+ident(age) operator(=) integer(18) comment(// binding "global" variable)
+ident(printAge)operator(()(\)) comment(// => 18)
+
+keyword(if) operator(()ident(age) operator(>) integer(0)(\)) operator({)
+ keyword(def) ident(origAge) operator(=) ident(age)
+ ident(age) operator(=) integer(23)
+ ident(printAge)operator(()(\)) comment(// => 23)
+ ident(age) operator(=) ident(origAge)
+(})
+ident(printAge)operator(()(\)) comment(// => 18)
+
+comment(// Depending on the circmstances we could enhance this in various ways)
+comment(// such as synchronizing, surrounding with try ... finally, using a)
+comment(// memento pattern, saving the whole binding, using a ThreadLocal ...)
+
+comment(// There is no need to use local(\) for filehandles or directory)
+comment(// handles in Groovy because filehandles are normal objects.)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.14)
+comment(//----------------------------------------------------------------------------------)
+comment(// Redefining a Function)
+comment(// This can be done via a number of ways:)
+
+comment(// OO approach:)
+comment(// The standard trick using OO is to override methods in subclasses)
+type(class) class(Parent) operator({) keyword(def) method(foo)operator(()(\))operator({) ident(println) string<delimiter(')content(foo)delimiter(')> (}) (})
+type(class) class(Child) directive(extends) ident(Parent) operator({) keyword(def) method(foo)operator(()(\))operator({) ident(println) string<delimiter(')content(bar)delimiter(')> (}) (})
+keyword(new) ident(Parent)operator(()(\))operator(.)ident(foo)operator(()(\)) comment(// => foo)
+keyword(new) ident(Child)operator(()(\))operator(.)ident(foo)operator(()(\)) comment(// => bar)
+
+comment(// Category approach:)
+comment(// If you want to redefine a method from an existing library)
+comment(// you can use categories. This can be done to avoid name conflicts)
+comment(// or to patch functionality with local mods without changing)
+comment(// original code)
+ident(println) keyword(new) pre_type(Date)operator(()(\))operator(.)ident(toString)operator(()(\))
+comment(// => Sat Jan 06 16:44:55 EST 2007)
+type(class) class(DateCategory) operator({)
+ directive(static) ident(toString)operator(()pre_type(Date) ident(self)(\)) operator({) string<delimiter(')content(not telling)delimiter(')> (})
+(})
+ident(use) operator(()ident(DateCategory)(\)) operator({)
+ ident(println) keyword(new) pre_type(Date)operator(()(\))operator(.)ident(toString)operator(()(\))
+(})
+comment(// => not telling)
+
+comment(// Closure approach:)
+comment(// Groovy's closures let you have "anonymous methods" as objects.)
+comment(// This allows you to be very flexible with "method" redefinition, e.g.:)
+ident(colors) operator(=) string<delimiter(')content(red yellow blue green)delimiter(')>operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))operator(.)ident(toList)operator(()(\))
+ident(color2html) operator(=) keyword(new) ident(Expando)operator(()(\))
+ident(colors)operator(.)ident(each) operator({) ident(c) operator(->)
+ ident(color2html)operator([)ident(c)(]) operator(=) operator({) ident(args) operator(->) string<delimiter(")content(<FONT COLOR=')inline<inline_delimiter($)ident(c)>content('>)inline<inline_delimiter($)ident(args)>content(</FONT>)delimiter(")> (})
+(})
+ident(println) ident(color2html)operator(.)ident(yellow)operator(()string<delimiter(')content(error)delimiter(')>(\))
+comment(// => <FONT COLOR='yellow'>error</FONT>)
+ident(color2html)operator(.)ident(yellow) operator(=) operator({) ident(args) operator(->) string<delimiter(")content(<b>)inline<inline_delimiter($)ident(args)>content(</b>)delimiter(")> (}) comment(// too hard to see yellow)
+ident(println) ident(color2html)operator(.)ident(yellow)operator(()string<delimiter(')content(error)delimiter(')>(\))
+comment(// => <b>error</b>)
+
+comment(// Other approaches:)
+comment(// you could use invokeMethod to intercept the original method and call)
+comment(// your modified method on just particular input data)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.15)
+comment(//----------------------------------------------------------------------------------)
+comment(// Trapping Undefined Function Calls)
+type(class) class(FontHelper) operator({)
+ comment(// we could define all the important colors explicitly like this)
+ keyword(def) method(pink)operator(()ident(info)(\)) operator({)
+ ident(buildFont)operator(()string<delimiter(')content(hot pink)delimiter(')>operator(,) ident(info)(\))
+ (})
+ comment(// but this method will catch any undefined ones)
+ keyword(def) method(invokeMethod)operator(()pre_type(String) ident(name)operator(,) pre_type(Object) ident(args)(\)) operator({)
+ ident(buildFont)operator(()ident(name)operator(,) ident(args)operator(.)ident(join)operator(()string<delimiter(')content( and )delimiter(')>(\)\))
+ (})
+ keyword(def) method(buildFont)operator(()ident(name)operator(,) ident(info)(\)) operator({)
+ string<delimiter(")content(<FONT COLOR=')inline<inline_delimiter($)ident(name)>content('>)delimiter(")> operator(+) ident(info) operator(+) string<delimiter(")content(</FONT>)delimiter(")>
+ (})
+(})
+ident(fh) operator(=) keyword(new) ident(FontHelper)operator(()(\))
+ident(println) ident(fh)operator(.)ident(pink)operator(()string<delimiter(")content(panther)delimiter(")>(\))
+ident(println) ident(fh)operator(.)ident(chartreuse)operator(()string<delimiter(")content(stuff)delimiter(")>operator(,) string<delimiter(")content(more stuff)delimiter(")>(\))
+comment(// =>)
+comment(// <FONT COLOR='hot pink'>panther</FONT>)
+comment(// <FONT COLOR='chartreuse'>stuff and more stuff</FONT>)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.16)
+comment(//----------------------------------------------------------------------------------)
+comment(// Simulating Nested Subroutimes: Using Closures within Methods)
+keyword(def) method(outer)operator(()ident(arg)(\)) operator({)
+ keyword(def) ident(x) operator(=) ident(arg) operator(+) integer(35)
+ ident(inner) operator(=) operator({) ident(x) operator(*) integer(19) (})
+ ident(x) operator(+) ident(inner)operator(()(\))
+(})
+keyword(assert) ident(outer)operator(()integer(10)(\)) operator(==) integer(900)
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_10.17)
+comment(//----------------------------------------------------------------------------------)
+comment(// Program: Sorting Your Mail)
+doctype(#!/usr/bin/groovy)
+keyword(import) include(javax.mail.*)
+
+comment(// solution using mstor package (mstor.sf.net\))
+ident(session) operator(=) ident(Session)operator(.)ident(getDefaultInstance)operator(()keyword(new) pre_type(Properties)operator(()(\)\))
+ident(store) operator(=) ident(session)operator(.)ident(getStore)operator(()keyword(new) ident(URLName)operator(()string<delimiter(')content(mstor:/path_to_your_mbox_directory)delimiter(')>(\)\))
+ident(store)operator(.)ident(connect)operator(()(\))
+
+comment(// read messages from Inbox)
+ident(inbox) operator(=) ident(store)operator(.)ident(defaultFolder)operator(.)ident(getFolder)operator(()string<delimiter(')content(Inbox)delimiter(')>(\))
+ident(inbox)operator(.)ident(open)operator(()ident(Folder)operator(.)ident(READ_ONLY)(\))
+ident(messages) operator(=) ident(inbox)operator(.)ident(messages)operator(.)ident(toList)operator(()(\))
+
+comment(// extractor closures)
+ident(subject) operator(=) operator({) ident(m) operator(->) ident(m)operator(.)ident(subject) (})
+ident(subjectExcludingReplyPrefix) operator(=) operator({) ident(m) operator(->) ident(subject)operator(()ident(m)(\))operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((?i\)Re:)char(\\\\)content(s*)delimiter(/)>operator(,)string<delimiter(')delimiter(')>(\)) (}) comment(// double slash to single outside triple quotes)
+ident(date) operator(=) operator({) ident(m) operator(->) ident(d) operator(=) ident(m)operator(.)ident(sentDate)operator(;) keyword(new) pre_type(Date)operator(()ident(d)operator(.)ident(year)operator(,) ident(d)operator(.)ident(month)operator(,) ident(d)operator(.)ident(date)(\)) (}) comment(// ignore time fields)
+
+comment(// sort by subject excluding 'Re:' prefixs then print subject for first 6)
+ident(println) ident(messages)operator(.)ident(sort)operator({)ident(subjectExcludingReplyPrefix)operator(()local_variable(it)(\)})operator([)integer(0)operator(..)integer(5)(])operator(*.)ident(subject)operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+comment(// =>)
+comment(// Additional Resources for JDeveloper 10g (10.1.3\))
+comment(// Amazon Web Services Developer Connection Newsletter #18)
+comment(// Re: Ant 1.7.0?)
+comment(// ARN Daily | 2007: IT predictions for the year ahead)
+comment(// Big Changes at Gentleware)
+comment(// BigPond Account Notification)
+
+comment(// sort by date then subject (print first 6 entries\))
+ident(sorted) operator(=) ident(messages)operator(.)ident(sort)operator({) ident(a)operator(,)ident(b) operator(->)
+ ident(date)operator(()ident(a)(\)) operator(==) ident(date)operator(()ident(b)(\)) operator(?)
+ ident(subjectExcludingReplyPrefix)operator(()ident(a)(\)) operator(<=)operator(>) ident(subjectExcludingReplyPrefix)operator(()ident(b)(\)) operator(:)
+ ident(date)operator(()ident(a)(\)) operator(<=)operator(>) ident(date)operator(()ident(b)(\))
+(})
+ident(sorted)operator([)integer(0)operator(..)integer(5)(])operator(.)ident(each)operator({) ident(m) operator(->) ident(println) string<delimiter(")inline<inline_delimiter($)ident(m)>content(.sentDate: )inline<inline_delimiter($)ident(m)>content(.subject)delimiter(")> (})
+comment(// =>)
+comment(// Wed Jan 03 08:54:15 EST 2007: ARN Daily | 2007: IT predictions for the year ahead)
+comment(// Wed Jan 03 15:33:31 EST 2007: EclipseSource: RCP Adoption, Where Art Thou?)
+comment(// Wed Jan 03 00:10:11 EST 2007: What's New at Sams Publishing?)
+comment(// Fri Jan 05 08:31:11 EST 2007: Building a Sustainable Open Source Business)
+comment(// Fri Jan 05 09:53:45 EST 2007: Call for Participation: Agile 2007)
+comment(// Fri Jan 05 05:51:36 EST 2007: IBM developerWorks Weekly Edition, 4 January 2007)
+
+comment(// group by date then print first 2 entries of first 2 dates)
+ident(groups) operator(=) ident(messages)operator(.)ident(groupBy)operator({) ident(date)operator(()local_variable(it)(\)) (})
+ident(groups)operator(.)ident(keySet)operator(()(\))operator(.)ident(toList)operator(()(\))operator([)integer(0)operator(..)integer(1)(])operator(.)ident(each)operator({)
+ ident(println) local_variable(it)
+ ident(println) ident(groups)operator([)local_variable(it)(])operator([)integer(0)operator(..)integer(1)(])operator(.)ident(collect)operator({) string<delimiter(')content( )delimiter(')> operator(+) local_variable(it)operator(.)ident(subject) (})operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+(})
+comment(// =>)
+comment(// Wed Jan 03 00:00:00 EST 2007)
+comment(// ARN Daily | 2007: IT predictions for the year ahead)
+comment(// EclipseSource: RCP Adoption, Where Art Thou?)
+comment(// Fri Jan 05 00:00:00 EST 2007)
+comment(// Building a Sustainable Open Source Business)
+comment(// Call for Participation: Agile 2007)
+
+
+comment(// @@PLEAC@@_11.0)
+comment(//----------------------------------------------------------------------------------)
+comment(// In Groovy, most usages of names are references (there are some special)
+comment(// rules for the map shorthand notation and builders\).)
+comment(// Objects are inherently anonymous, they don't know what names refer to them.)
+ident(ref) operator(=) integer(3) comment(// points ref to an Integer object with value 3.)
+ident(println) ident(ref) comment(// prints the value that the name ref refers to.)
+
+ident(myList) operator(=) operator([)integer(3)operator(,) integer(4)operator(,) integer(5)(]) comment(// myList is a name for this list)
+ident(anotherRef) operator(=) ident(myList)
+ident(myMap) operator(=) operator([)string<delimiter(")content(How)delimiter(")>operator(:) string<delimiter(")content(Now)delimiter(")>operator(,) string<delimiter(")content(Brown)delimiter(")>operator(:) string<delimiter(")content(Cow)delimiter(")>(]) comment(// myMap is a name for this map)
+
+ident(anArray) operator(=) operator([)integer(1)operator(,) integer(2)operator(,) integer(3)(]) keyword(as) type(int)type([]) comment(// creates an array of three references to Integer objects)
+
+ident(list) operator(=) operator([)type([])(]) comment(// a list containing an empty list)
+ident(list)operator([)integer(2)(]) operator(=) string<delimiter(')content(Cat)delimiter(')>
+ident(println) ident(list) comment(// => [[], null, "Cat"])
+ident(list)operator([)integer(0)(])operator([)integer(2)(]) operator(=) string<delimiter(')content(Dog)delimiter(')>
+ident(println) ident(list) comment(// => [[null, null, "Dog"], null, "Cat"])
+
+ident(a) operator(=) operator([)integer(2)operator(,) integer(1)(])
+ident(b) operator(=) ident(a) comment(// b is a reference to the same thing as a)
+ident(a)operator(.)ident(sort)operator(()(\))
+ident(println) ident(b) comment(// => [1, 2])
+
+ident(nat) operator(=) operator([) pre_type(Name)operator(:) string<delimiter(")content(Leonhard Euler)delimiter(")>operator(,)
+ key(Address)operator(:) string<delimiter(")content(1729 Ramanujan Lane)char(\\n)content(Mathworld, PI 31416)delimiter(")>operator(,)
+ key(Birthday)operator(:) hex(0x5bb5580)
+(])
+ident(println) ident(nat)
+comment(// =>["Address":"1729 Ramanujan Lane\\nMathworld, PI 31416", "Name":"Leonhard Euler", "Birthday":96163200])
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.1)
+comment(//----------------------------------------------------------------------------------)
+ident(aref) operator(=) ident(myList)
+ident(anonList) operator(=) operator([)integer(1)operator(,) integer(3)operator(,) integer(5)operator(,) integer(7)operator(,) integer(9)(])
+ident(anonCopy) operator(=) ident(anonList)
+ident(implicitCreation) operator(=) operator([)integer(2)operator(,) integer(4)operator(,) integer(6)operator(,) integer(8)operator(,) integer(10)(])
+
+ident(anonList) operator(+=) integer(11)
+ident(println) ident(anonList) comment(// => [1, 3, 5, 7, 9, 11])
+
+ident(two) operator(=) ident(implicitCreation)operator([)integer(0)(])
+keyword(assert) ident(two) operator(==) integer(2)
+
+comment(// To get the last index of a list, you can use size(\))
+comment(// but you never would)
+ident(lastIdx) operator(=) ident(aref)operator(.)ident(size)operator(()(\)) operator(-) integer(1)
+
+comment(// Normally, though, you'd use an index of -1 for the last)
+comment(// element, -2 for the second last, etc.)
+ident(println) ident(implicitCreation)operator([)operator(-)integer(1)(])
+comment(//=> 10)
+
+comment(// And if you were looping through (and not using a list closure operator\))
+operator(()integer(0)operator(..<)ident(aref)operator(.)ident(size)operator(()(\)\))operator(.)ident(each)operator({) comment(/* do something */) (})
+
+ident(numItems) operator(=) ident(aref)operator(.)ident(size)operator(()(\))
+
+keyword(assert) ident(anArray) keyword(instanceof) type(int)type([])
+keyword(assert) ident(anArray)operator(.)ident(class)operator(.)ident(isArray)operator(()(\))
+ident(println) ident(anArray)
+
+ident(myList)operator(.)ident(sort)operator(()(\)) comment(// sort is in place.)
+ident(myList) operator(+=) string<delimiter(")content(an item)delimiter(")> comment(// append item)
+
+keyword(def) method(createList)operator(()(\)) operator({) keyword(return) type([]) (})
+ident(aref1) operator(=) ident(createList)operator(()(\))
+ident(aref2) operator(=) ident(createList)operator(()(\))
+comment(// aref1 and aref2 point to different lists.)
+
+ident(println) ident(anonList)operator([)integer(4)(]) comment(// refers to the 4th item in the list_ref list.)
+
+comment(// The following two statements are equivalent and return up to 3 elements)
+comment(// at indices 3, 4, and 5 (if they exist\).)
+ident(x) operator(=) ident(anonList)operator([)integer(3)operator(..)integer(5)(])
+ident(x) operator(=) ident(anonList)operator([)operator(()integer(3)operator(..)integer(5)(\))operator(.)ident(step)operator(()integer(1)(\)])
+
+comment(// This will insert 3 elements, overwriting elements at indices 3,4, or 5 - if they exist.)
+ident(anonList)operator([)integer(3)operator(..)integer(5)(]) operator(=) operator([)string<delimiter(")content(blackberry)delimiter(")>operator(,) string<delimiter(")content(blueberry)delimiter(")>operator(,) string<delimiter(")content(pumpkin)delimiter(")>(])
+
+comment(// non index-based looping)
+keyword(for) operator(()ident(item) keyword(in) ident(anonList)(\)) ident(println) ident(item)
+ident(anonList)operator(.)ident(each)operator({) ident(println) local_variable(it) (})
+
+comment(// index-based looping)
+operator(()integer(0)operator(..<)ident(anonList)operator(.)ident(size)operator(()(\)\))operator(.)ident(each)operator({) ident(idx) operator(->) ident(println) ident(anonList)operator([)ident(idx)(]) (})
+keyword(for) operator(()ident(idx) keyword(in) integer(0)operator(..<)ident(anonList)operator(.)ident(size)operator(()(\)\)) ident(println) ident(anonList)operator([)ident(idx)(])
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// Making Hashes of Arrays)
+ident(hash) operator(=) operator([)operator(:)(]) comment(// empty map)
+ident(hash)operator([)string<delimiter(")content(KEYNAME)delimiter(")>(]) operator(=) string<delimiter(")content(new value)delimiter(")>
+
+ident(hash)operator(.)ident(each)operator({) ident(key)operator(,) ident(value) operator(->) ident(println) ident(key) operator(+) string<delimiter(')content( )delimiter(')> operator(+) ident(value) (})
+
+ident(hash)operator([)string<delimiter(")content(a key)delimiter(")>(]) operator(=) operator([)integer(3)operator(,) integer(4)operator(,) integer(5)(])
+ident(values) operator(=) ident(hash)operator([)string<delimiter(")content(a key)delimiter(")>(])
+
+ident(hash)operator([)string<delimiter(")content(a key)delimiter(")>(]) operator(+=) integer(6)
+ident(println) ident(hash)
+comment(// => ["KEYNAME":"new value", "a key":[3, 4, 5, 6]])
+
+comment(// attempting to access a value for a key not in the map yields null)
+keyword(assert) ident(hash)operator([)string<delimiter(')content(unknown key)delimiter(')>(]) operator(==) keyword(null)
+keyword(assert) ident(hash)operator(.)ident(get)operator(()string<delimiter(')content(unknown key)delimiter(')>operator(,) integer(45)(\)) operator(==) integer(45)
+ident(println) ident(hash)
+comment(// => ["unknown key":45, "KEYNAME":"new value", "a key":[3, 4, 5, 6]])
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// Hashes are no different to other objects)
+ident(myHash) operator(=) operator([) key(key1)operator(:)integer(100)operator(,) key(key2)operator(:)integer(200) (])
+ident(myHashCopy) operator(=) ident(myHash)operator(.)ident(clone)operator(()(\))
+
+ident(value) operator(=) ident(myHash)operator([)string<delimiter(')content(key1)delimiter(')>(])
+ident(value) operator(=) ident(myHash)operator(.)string<delimiter(')content(key1)delimiter(')>
+ident(slice) operator(=) ident(myHash)operator([)integer(1)operator(..)integer(3)(])
+ident(keys) operator(=) ident(myHash)operator(.)ident(keySet)operator(()(\))
+
+keyword(assert) ident(myHash) keyword(instanceof) pre_type(Map)
+
+operator([)ident(myHash)operator(,) ident(hash)(])operator(.)ident(each)operator({) ident(m) operator(->)
+ ident(m)operator(.)ident(each)operator({) ident(k)operator(,) ident(v) operator(->) ident(println) string<delimiter(")inline<inline_delimiter($)ident(k)>content( => )inline<inline_delimiter($)ident(v)>delimiter(")>(})
+(})
+comment(// =>)
+comment(// key1 => 100)
+comment(// key2 => 200)
+comment(// unknown key => 45)
+comment(// KEYNAME => new value)
+comment(// a key => [3, 4, 5, 6])
+
+ident(values) operator(=) operator([)string<delimiter(')content(key1)delimiter(')>operator(,)string<delimiter(')content(key2)delimiter(')>(])operator(.)ident(collect)operator({) ident(myHash)operator([)local_variable(it)(]) (})
+ident(println) ident(values) comment(// => [100, 200])
+
+keyword(for) operator(()ident(key) keyword(in) operator([)string<delimiter(")content(key1)delimiter(")>operator(,) string<delimiter(")content(key2)delimiter(")>(]\)) operator({)
+ ident(myHash)operator([)ident(key)(]) operator(+=) integer(7)
+(})
+ident(println) ident(myHash) comment(// => ["key1":107, "key2":207])
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// you can use closures or the &method notation)
+keyword(def) method(joy)operator(()(\)) operator({) ident(println) string<delimiter(')content(joy)delimiter(')> (})
+keyword(def) method(sullen)operator(()(\)) operator({) ident(println) string<delimiter(')content(sullen)delimiter(')> (})
+ident(angry) operator(=) operator({) ident(println) string<delimiter(')content(angry)delimiter(')> (})
+ident(commands) operator(=) operator([)key(happy)operator(:) local_variable(this)operator(.)operator(&)ident(joy)operator(,)
+ key(sad)operator(:) local_variable(this)operator(.)operator(&)ident(sullen)operator(,)
+ key(done)operator(:) operator({) pre_type(System)operator(.)ident(exit)operator(()integer(0)(\)) (})operator(,)
+ key(mad)operator(:) ident(angry)
+(])
+
+ident(print) string<delimiter(")content(How are you?)delimiter(")>
+ident(cmd) operator(=) pre_type(System)operator(.)ident(in)operator(.)ident(readLine)operator(()(\))
+keyword(if) operator(()ident(cmd) keyword(in) ident(commands)operator(.)ident(keySet)operator(()(\)\)) ident(commands)operator([)ident(cmd)(])operator(()(\))
+keyword(else) ident(println) string<delimiter(")content(No such command: )inline<inline_delimiter($)ident(cmd)>delimiter(")>
+
+
+comment(// a counter of the type referred to in the original cookbook)
+comment(// would be implemented using a class)
+keyword(def) method(counterMaker)operator(()(\))operator({)
+ keyword(def) ident(start) operator(=) integer(0)
+ keyword(return) operator({) operator(->) ident(start)operator(++)operator(;) ident(start)operator(-)integer(1) (})
+(})
+
+ident(counter) operator(=) ident(counterMaker)operator(()(\))
+integer(5)operator(.)ident(times)operator({) ident(print) string<delimiter(")inline<inline_delimiter(${)ident(counter(\))inline_delimiter(})>content( )delimiter(")> (})operator(;) ident(println)operator(()(\))
+
+ident(counter1) operator(=) ident(counterMaker)operator(()(\))
+ident(counter2) operator(=) ident(counterMaker)operator(()(\))
+
+integer(5)operator(.)ident(times)operator({) ident(println) string<delimiter(")inline<inline_delimiter(${)ident(counter1(\))inline_delimiter(})>content( )delimiter(")> (})
+ident(println) string<delimiter(")inline<inline_delimiter(${)ident(counter1(\))inline_delimiter(})>content( )inline<inline_delimiter(${)ident(counter2(\))inline_delimiter(})>delimiter(")>
+comment(//=> 0)
+comment(//=> 1)
+comment(//=> 2)
+comment(//=> 3)
+comment(//=> 4)
+comment(//=> 5 0)
+
+
+keyword(def) method(timestamp)operator(()(\)) operator({)
+ keyword(def) ident(start) operator(=) pre_type(System)operator(.)ident(currentTimeMillis)operator(()(\))
+ keyword(return) operator({) operator(()pre_type(System)operator(.)ident(currentTimeMillis)operator(()(\)) operator(-) ident(start)(\))operator(.)ident(intdiv)operator(()integer(1000)(\)) (})
+(})
+ident(early) operator(=) ident(timestamp)operator(()(\))
+comment(//sleep(10000\))
+ident(later) operator(=) ident(timestamp)operator(()(\))
+ident(sleep)operator(()integer(2000)(\))
+ident(println) string<delimiter(")content(It's been )inline<inline_delimiter(${)ident(early(\))inline_delimiter(})>content( seconds since early.)delimiter(")>
+ident(println) string<delimiter(")content(It's been )inline<inline_delimiter(${)ident(later(\))inline_delimiter(})>content( seconds since later.)delimiter(")>
+comment(//=> It's been 12 seconds since early.)
+comment(//=> It's been 2 seconds since later.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// All variables in Groovy are objects including primitives. Some objects)
+comment(// are immutable. Some operations on objects change mutable objects.)
+comment(// Some operations produce new objects.)
+
+comment(// 15 is an Integer which is an immutable object.)
+comment(// passing 15 to a method passes a reference to the Integer object.)
+keyword(def) method(print)operator(()ident(n)(\)) operator({) ident(println) string<delimiter(")inline<inline_delimiter(${)ident(n.toString(\))inline_delimiter(})>delimiter(")> (})
+ident(print)operator(()integer(15)(\)) comment(// no need to create any kind of explicit reference)
+
+comment(// even though Integers are immutable, references to them are not)
+ident(x) operator(=) integer(1)
+ident(y) operator(=) ident(x)
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(x)>content( )inline<inline_delimiter($)ident(y)>delimiter(")> comment(// => 1 1)
+ident(x) operator(+=) integer(1) comment(// "x" now refers to a different object than y)
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(x)>content( )inline<inline_delimiter($)ident(y)>delimiter(")> comment(// => 2 1)
+ident(y) operator(=) integer(4) comment(// "y" now refers to a different object than it did before)
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(x)>content( )inline<inline_delimiter($)ident(y)>delimiter(")> comment(// => 2 4)
+
+comment(// Some objects (including ints and strings\) are immutable, however, which)
+comment(// can give the illusion of a by-value/by-reference distinction:)
+ident(list) operator(=) operator([)operator([)integer(1)(])operator(,) integer(1)operator(,) string<delimiter(')content(s)delimiter(')>(])
+ident(list)operator(.)ident(each)operator({) local_variable(it) operator(+=) integer(1) (}) comment(// plus operator doesn't operate inplace)
+ident(print) ident(list) comment(//=> [[1] 1 s])
+ident(list) operator(=) ident(list)operator(.)ident(collect)operator({) local_variable(it) operator(+) integer(1) (})
+ident(print) ident(list) comment(//=> [[1, 1], 2, s1])
+
+ident(list) operator(=) operator([)operator([)string<delimiter(')content(Z)delimiter(')>operator(,) string<delimiter(')content(Y)delimiter(')>operator(,) string<delimiter(')content(X)delimiter(')>(])operator(,) operator([)string<delimiter(')content(C)delimiter(')>operator(,) string<delimiter(')content(B)delimiter(')>operator(,) string<delimiter(')content(A)delimiter(')>(])operator(,) operator([)integer(5)operator(,) integer(3)operator(,) integer(1)(]])
+ident(list)operator(.)ident(each)operator({) local_variable(it)operator(.)ident(sort)operator(()(\)) (}) comment(// sort operation operates inline)
+ident(println) ident(list) comment(// => [["X", "Y", "Z"], ["A", "B", "C"], [1, 3, 5]])
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// As indicated by the previous section, everything is referenced, so)
+comment(// just create a list as normal, and beware that augmented assignment)
+comment(// works differently with immutable objects to mutable ones and depends)
+comment(// on the semantics of the particular operation invoked:)
+ident(mylist) operator(=) operator([)integer(1)operator(,) string<delimiter(")content(s)delimiter(")>operator(,) operator([)integer(1)(]])
+ident(print) ident(mylist)
+comment(//=> [1, s, [1]])
+
+ident(mylist)operator(.)ident(each)operator({) local_variable(it) operator(*=) integer(2) (})
+ident(print) ident(mylist)
+comment(//=> [1, s, [1,1]])
+
+ident(mylist)operator([)integer(0)(]) operator(*=) integer(2)
+ident(mylist)operator([)operator(-)integer(1)(]) operator(*=) integer(2)
+ident(print) ident(mylist)
+comment(//=> [2, s, [1, 1]])
+
+comment(// If you need to modify every value in a list, you use collect)
+comment(// which does NOT modify inplace but rather returns a new collection:)
+ident(mylist) operator(=) integer(1)operator(..)integer(4)
+ident(println) ident(mylist)operator(.)ident(collect)operator({) local_variable(it)operator(**)integer(3) operator(*) integer(4)operator(/)integer(3) operator(*) pre_type(Math)operator(.)ident(PI) (})
+comment(// => [4.188790204681671, 33.510321638395844, 113.09733552923255, 268.0825731062243])
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.7)
+comment(//----------------------------------------------------------------------------------)
+keyword(def) method(mkcounter)operator(()ident(count)(\)) operator({)
+ keyword(def) ident(start) operator(=) ident(count)
+ keyword(def) ident(bundle) operator(=) operator([)operator(:)(])
+ ident(bundle)operator(.)string<delimiter(')content(NEXT)delimiter(')> operator(=) operator({) ident(count) operator(+=) integer(1) (})
+ ident(bundle)operator(.)string<delimiter(')content(PREV)delimiter(')> operator(=) operator({) ident(count) operator(-=) integer(1) (})
+ ident(bundle)operator(.)string<delimiter(')content(RESET)delimiter(')> operator(=) operator({) ident(count) operator(=) ident(start) (})
+ ident(bundle)operator([)string<delimiter(")content(LAST)delimiter(")>(]) operator(=) ident(bundle)operator([)string<delimiter(")content(PREV)delimiter(")>(])
+ keyword(return) ident(bundle)
+(})
+
+ident(c1) operator(=) ident(mkcounter)operator(()integer(20)(\))
+ident(c2) operator(=) ident(mkcounter)operator(()integer(77)(\))
+
+ident(println) string<delimiter(")content(next c1: )inline<inline_delimiter(${)ident(c1["NEXT"](\))inline_delimiter(})>delimiter(")> comment(// 21)
+ident(println) string<delimiter(")content(next c2: )inline<inline_delimiter(${)ident(c2["NEXT"](\))inline_delimiter(})>delimiter(")> comment(// 78)
+ident(println) string<delimiter(")content(next c1: )inline<inline_delimiter(${)ident(c1["NEXT"](\))inline_delimiter(})>delimiter(")> comment(// 22)
+ident(println) string<delimiter(")content(last c1: )inline<inline_delimiter(${)ident(c1["PREV"](\))inline_delimiter(})>delimiter(")> comment(// 21)
+ident(println) string<delimiter(")content(last c1: )inline<inline_delimiter(${)ident(c1["LAST"](\))inline_delimiter(})>delimiter(")> comment(// 20)
+ident(println) string<delimiter(")content(old c2: )inline<inline_delimiter(${)ident(c2["RESET"](\))inline_delimiter(})>delimiter(")> comment(// 77)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.8)
+comment(//----------------------------------------------------------------------------------)
+keyword(def) method(addAndMultiply)operator(()ident(a)operator(,) ident(b)(\)) operator({)
+ ident(println) string<delimiter(")inline<inline_delimiter(${)ident(a+b)inline_delimiter(})>content( )inline<inline_delimiter(${)ident(a*b)inline_delimiter(})>delimiter(")>
+(})
+ident(methRef) operator(=) local_variable(this)operator(.)operator(&)ident(addAndMultiply)
+comment(// or use direct closure)
+ident(multiplyAndAdd) operator(=) operator({) ident(a)operator(,)ident(b) operator(->) ident(println) string<delimiter(")inline<inline_delimiter(${)ident(a*b)inline_delimiter(})>content( )inline<inline_delimiter(${)ident(a+b)inline_delimiter(})>delimiter(")> (})
+comment(// later ...)
+ident(methRef)operator(()integer(2)operator(,)integer(3)(\)) comment(// => 5 6)
+ident(multiplyAndAdd)operator(()integer(2)operator(,)integer(3)(\)) comment(// => 6 5)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.9)
+comment(//----------------------------------------------------------------------------------)
+ident(record) operator(=) operator([)
+ string<delimiter(")content(name)delimiter(")>operator(:) string<delimiter(")content(Jason)delimiter(")>operator(,)
+ string<delimiter(")content(empno)delimiter(")>operator(:) integer(132)operator(,)
+ string<delimiter(")content(title)delimiter(")>operator(:) string<delimiter(")content(deputy peon)delimiter(")>operator(,)
+ string<delimiter(")content(age)delimiter(")>operator(:) integer(23)operator(,)
+ string<delimiter(")content(salary)delimiter(")>operator(:) integer(37000)operator(,)
+ string<delimiter(")content(pals)delimiter(")>operator(:) operator([)string<delimiter(")content(Norbert)delimiter(")>operator(,) string<delimiter(")content(Rhys)delimiter(")>operator(,) string<delimiter(")content(Phineas)delimiter(")>(])operator(,)
+(])
+ident(println) string<delimiter(")content(I am )inline<inline_delimiter(${)ident(record.'name')inline_delimiter(})>content(, and my pals are )inline<inline_delimiter(${)ident(record.'pals'.join(', '\))inline_delimiter(})>content(.)delimiter(")>
+comment(// => I am Jason, and my pals are Norbert, Rhys, Phineas.)
+
+ident(byname) operator(=) operator([)operator(:)(])
+ident(byname)operator([)ident(record)operator([)string<delimiter(")content(name)delimiter(")>(]]) operator(=) ident(record)
+
+ident(rp) operator(=) ident(byname)operator(.)ident(get)operator(()string<delimiter(")content(Aron)delimiter(")>(\))
+keyword(if) operator(()ident(rp)(\)) ident(println) string<delimiter(")content(Aron is employee )inline<inline_delimiter(${)ident(rp["empno"])inline_delimiter(})>content(.)delimiter(")>
+
+ident(byname)operator([)string<delimiter(")content(Jason)delimiter(")>(])operator([)string<delimiter(")content(pals)delimiter(")>(]) operator(+=) string<delimiter(")content(Theodore)delimiter(")>
+ident(println) string<delimiter(")content(Jason now has )inline<inline_delimiter(${)ident(byname['Jason']['pals'].size(\))inline_delimiter(})>content( pals.)delimiter(")>
+
+ident(byname)operator(.)ident(each)operator({) ident(name)operator(,) ident(record) operator(->)
+ ident(println) string<delimiter(")inline<inline_delimiter($)ident(name)>content( is employee number )inline<inline_delimiter(${)ident(record['empno'])inline_delimiter(})>content(.)delimiter(")>
+(})
+
+ident(employees) operator(=) operator([)operator(:)(])
+ident(employees)operator([)ident(record)operator([)string<delimiter(")content(empno)delimiter(")>(]]) operator(=) ident(record)
+
+comment(// lookup by id)
+ident(rp) operator(=) ident(employees)operator([)integer(132)(])
+keyword(if) operator(()ident(rp)(\)) ident(println) string<delimiter(")content(Employee number 132 is )inline<inline_delimiter(${)ident(rp.'name')inline_delimiter(})>content(.)delimiter(")>
+
+ident(byname)operator([)string<delimiter(")content(Jason)delimiter(")>(])operator([)string<delimiter(")content(salary)delimiter(")>(]) operator(*=) float(1.035)
+ident(println) ident(record)
+comment(// => ["pals":["Norbert", "Rhys", "Phineas", "Theodore"], "age":23,)
+comment(// "title":"deputy peon", "name":"Jason", "salary":38295.000, "empno":132])
+
+ident(peons) operator(=) ident(employees)operator(.)ident(findAll)operator({) ident(k)operator(,) ident(v) operator(->) ident(v)operator(.)string<delimiter(')content(title)delimiter(')> operator(=~) regexp<delimiter(/)content((?i\)peon)delimiter(/)> (})
+keyword(assert) ident(peons)operator(.)ident(size)operator(()(\)) operator(==) integer(1)
+ident(tsevens) operator(=) ident(employees)operator(.)ident(findAll)operator({) ident(k)operator(,) ident(v) operator(->) ident(v)operator(.)string<delimiter(')content(age)delimiter(')> operator(==) integer(27) (})
+keyword(assert) ident(tsevens)operator(.)ident(size)operator(()(\)) operator(==) integer(0)
+
+comment(// Go through all records)
+ident(println) string<delimiter(')content(Names are: )delimiter(')> operator(+) ident(employees)operator(.)ident(values)operator(()(\))operator(.)ident(collect)operator({)ident(r)operator(->)ident(r)operator(.)string<delimiter(')content(name)delimiter(')>(})operator(.)ident(join)operator(()string<delimiter(')content(, )delimiter(')>(\))
+
+ident(byAge) operator(=) operator({)ident(a)operator(,)ident(b)operator(->) ident(a)operator(.)ident(value)operator(()(\))operator(.)string<delimiter(')content(age)delimiter(')> operator(<=)operator(>) ident(b)operator(.)ident(value)operator(()(\))operator(.)string<delimiter(')content(age)delimiter(')>(})
+ident(employees)operator(.)ident(values)operator(()(\))operator(.)ident(sort)operator({)ident(byAge)(})operator(.)ident(each)operator({) ident(r)operator(->)
+ ident(println) string<delimiter(")inline<inline_delimiter(${)ident(r.'name')inline_delimiter(})>content( is )inline<inline_delimiter(${)ident(r.'age')inline_delimiter(})>delimiter(")>
+(})
+
+comment(// byage, a hash: age => list of records)
+ident(byage) operator(=) operator([)operator(:)(])
+ident(byage)operator([)ident(record)operator([)string<delimiter(")content(age)delimiter(")>(]]) operator(=) ident(byage)operator(.)ident(get)operator(()ident(record)operator([)string<delimiter(")content(age)delimiter(")>(])operator(,) type([])(\)) operator(+) operator([)ident(record)(])
+
+ident(byage)operator(.)ident(each)operator({) ident(age)operator(,) ident(list) operator(->)
+ ident(println) string<delimiter(")content(Age )inline<inline_delimiter($)ident(age)>content(: )inline<inline_delimiter(${)ident(list.collect{it.'name')inline_delimiter(})>content(.join(', '\)})delimiter(")>
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.10)
+comment(//----------------------------------------------------------------------------------)
+comment(// if you are using a Properties (see 8.16\) then just use load)
+comment(// and store (or storeToXML\))
+comment(// variation to original cookbook as Groovy can use Java's object serialization)
+ident(map) operator(=) operator([)integer(1)operator(:)string<delimiter(')content(Jan)delimiter(')>operator(,) integer(2)operator(:)string<delimiter(')content(Feb)delimiter(')>operator(,) integer(3)operator(:)string<delimiter(')content(Mar)delimiter(')>(])
+comment(// write)
+keyword(new) pre_type(File)operator(()string<delimiter(')content(months.dat)delimiter(')>(\))operator(.)ident(withObjectOutputStream)operator({) ident(oos) operator(->)
+ ident(oos)operator(.)ident(writeObject)operator(()ident(map)(\))
+(})
+comment(// reset)
+ident(map) operator(=) keyword(null)
+comment(// read)
+keyword(new) pre_type(File)operator(()string<delimiter(')content(months.dat)delimiter(')>(\))operator(.)ident(withObjectInputStream)operator({) ident(ois) operator(->)
+ ident(map) operator(=) ident(ois)operator(.)ident(readObject)operator(()(\))
+(})
+ident(println) ident(map) comment(// => [1:"Jan", 2:"Feb", 3:"Mar"])
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy automatically does pretty printing for some of the key types, e.g.)
+ident(mylist) operator(=) operator([)operator([)integer(1)operator(,)integer(2)operator(,)integer(3)(])operator(,) operator([)integer(4)operator(,) operator([)integer(5)operator(,)integer(6)operator(,)integer(7)(])operator(,) integer(8)operator(,)integer(9)operator(,) operator([)integer(0)operator(,)integer(3)operator(,)integer(5)(]])operator(,) integer(7)operator(,) integer(8)(])
+ident(println) ident(mylist)
+comment(// => [[1, 2, 3], [4, [5, 6, 7], 8, 9, [0, 3, 5]], 7, 8])
+
+ident(mydict) operator(=) operator([)string<delimiter(")content(abc)delimiter(")>operator(:) string<delimiter(")content(def)delimiter(")>operator(,) string<delimiter(")content(ghi)delimiter(")>operator(:)operator([)integer(1)operator(,)integer(2)operator(,)integer(3)(]])
+ident(println) ident(mydict)
+comment(// => ["abc":"def", "ghi":[1, 2, 3]])
+
+comment(// if you have another type of object you can use the built-in dump(\) method)
+type(class) class(PetLover) operator({)
+ keyword(def) ident(name)
+ keyword(def) ident(age)
+ keyword(def) ident(pets)
+(})
+ident(p) operator(=) keyword(new) ident(PetLover)operator(()key(name)operator(:)string<delimiter(')content(Jason)delimiter(')>operator(,) key(age)operator(:)integer(23)operator(,) key(pets)operator(:)operator([)key(dog)operator(:)string<delimiter(')content(Rover)delimiter(')>operator(,)key(cat)operator(:)string<delimiter(')content(Garfield)delimiter(')>(]\))
+ident(println) ident(p)
+comment(// => PetLover@b957ea)
+ident(println) ident(p)operator(.)ident(dump)operator(()(\))
+comment(// => <PetLover@b957ea name=Jason age=23 pets=["cat":"Garfield", "dog":"Rover"]>)
+
+comment(// If that isn't good enough, you can use Boost (http://tara-indigo.org/daisy/geekscape/g2/128\))
+comment(// or Jakarta Commons Lang *ToStringBuilders (jakarta.apache.org/commons\))
+comment(// Here's an example of Boost, just extend the supplied Primordial class)
+keyword(import) include(au.net.netstorm.boost.primordial.Primordial)
+type(class) class(PetLover2) directive(extends) ident(Primordial) operator({) keyword(def) ident(name)operator(,) ident(age)operator(,) ident(pets) (})
+ident(println) keyword(new) ident(PetLover2)operator(()key(name)operator(:)string<delimiter(')content(Jason)delimiter(')>operator(,) key(age)operator(:)integer(23)operator(,) key(pets)operator(:)operator([)key(dog)operator(:)string<delimiter(')content(Rover)delimiter(')>operator(,)key(cat)operator(:)string<delimiter(')content(Garfield)delimiter(')>(]\))
+comment(// =>)
+comment(// PetLover2[)
+comment(// name=Jason)
+comment(// age=23)
+comment(// pets={cat=Garfield, dog=Rover})
+comment(// metaClass=groovy.lang.MetaClassImpl@1d8d39f[class PetLover2])
+comment(// ])
+
+comment(// using Commons Lang ReflectionToStringBuilder (equivalent to dump(\)\))
+keyword(import) include(org.apache.commons.lang.builder.*)
+type(class) class(PetLover3) operator({)
+ keyword(def) ident(name)operator(,) ident(age)operator(,) ident(pets)
+ pre_type(String) ident(toString)operator(()(\)) operator({)
+ ident(ReflectionToStringBuilder)operator(.)ident(toString)operator(()local_variable(this)(\))
+ (})
+(})
+ident(println) keyword(new) ident(PetLover3)operator(()key(name)operator(:)string<delimiter(')content(Jason)delimiter(')>operator(,) key(age)operator(:)integer(23)operator(,) key(pets)operator(:)operator([)key(dog)operator(:)string<delimiter(')content(Rover)delimiter(')>operator(,)key(cat)operator(:)string<delimiter(')content(Garfield)delimiter(')>(]\))
+comment(// => PetLover3@196e136[name=Jason,age=23,pets={cat=Garfield, dog=Rover}])
+
+comment(// using Commons Lang ToStringBuilder if you want a custom format)
+type(class) class(PetLover4) operator({)
+ keyword(def) ident(name)operator(,) ident(dob)operator(,) ident(pets)
+ pre_type(String) ident(toString)operator(()(\)) operator({)
+ keyword(def) ident(d1) operator(=) ident(dob)operator(.)ident(time)operator(;) keyword(def) ident(d2) operator(=) operator(()keyword(new) pre_type(Date)operator(()(\)\))operator(.)ident(time)
+ type(int) ident(age) operator(=) operator(()ident(d2) operator(-) ident(d1)(\))operator(/)integer(1000)operator(/)integer(60)operator(/)integer(60)operator(/)integer(24)operator(/)integer(365) comment(// close approx good enough here)
+ keyword(return) keyword(new) ident(ToStringBuilder)operator(()local_variable(this)(\))operator(.)
+ ident(append)operator(()string<delimiter(")content(Pet Lover's name)delimiter(")>operator(,) ident(name)(\))operator(.)
+ ident(append)operator(()string<delimiter(')content(Pets)delimiter(')>operator(,) ident(pets)(\))operator(.)
+ ident(append)operator(()string<delimiter(')content(Age)delimiter(')>operator(,) ident(age)(\))
+ (})
+(})
+ident(println) keyword(new) ident(PetLover4)operator(()key(name)operator(:)string<delimiter(')content(Jason)delimiter(')>operator(,) key(dob)operator(:)keyword(new) pre_type(Date)operator(()integer(83)operator(,)oct(03)operator(,)oct(04)(\))operator(,) key(pets)operator(:)operator([)key(dog)operator(:)string<delimiter(')content(Rover)delimiter(')>operator(,)key(cat)operator(:)string<delimiter(')content(Garfield)delimiter(')>(]\))
+comment(// => PetLover4@fdfc58[Pet Lover's name=Jason,Pets={cat=Garfield, dog=Rover},Age=23])
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.12)
+comment(//----------------------------------------------------------------------------------)
+ident(oldlist) operator(=) operator([)integer(1)operator(,) integer(2)operator(,) integer(3)(])
+ident(newlist) operator(=) keyword(new) pre_type(ArrayList)operator(()ident(oldlist)(\)) comment(// shallow copy)
+ident(newlist) operator(=) ident(oldlist)operator(.)ident(clone)operator(()(\)) comment(// shallow copy)
+
+ident(oldmap) operator(=) operator([)key(a)operator(:)integer(1)operator(,) key(b)operator(:)integer(2)operator(,) key(c)operator(:)integer(3)(])
+ident(newmap) operator(=) keyword(new) pre_type(HashMap)operator(()ident(oldmap)(\)) comment(// shallow copy)
+ident(newmap) operator(=) ident(oldmap)operator(.)ident(clone)operator(()(\)) comment(// shallow copy)
+
+ident(oldarray) operator(=) operator([)integer(1)operator(,) integer(2)operator(,) integer(3)(]) keyword(as) type(int)type([])
+ident(newarray) operator(=) ident(oldarray)operator(.)ident(clone)operator(()(\))
+
+comment(// shallow copies copy a data structure, but don't copy the items in those)
+comment(// data structures so if there are nested data structures, both copy and)
+comment(// original will refer to the same object)
+ident(mylist) operator(=) operator([)string<delimiter(")content(1)delimiter(")>operator(,) string<delimiter(")content(2)delimiter(")>operator(,) string<delimiter(")content(3)delimiter(")>(])
+ident(newlist) operator(=) ident(mylist)operator(.)ident(clone)operator(()(\))
+ident(mylist)operator([)integer(0)(]) operator(=) string<delimiter(")content(0)delimiter(")>
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(mylist)>content( )inline<inline_delimiter($)ident(newlist)>delimiter(")>
+comment(//=> ["0", "2", "3"] ["1", "2", "3"])
+
+ident(mylist) operator(=) operator([)operator([)string<delimiter(")content(1)delimiter(")>operator(,) string<delimiter(")content(2)delimiter(")>operator(,) string<delimiter(")content(3)delimiter(")>(])operator(,) integer(4)(])
+ident(newlist) operator(=) ident(mylist)operator(.)ident(clone)operator(()(\))
+ident(mylist)operator([)integer(0)(])operator([)integer(0)(]) operator(=) string<delimiter(")content(0)delimiter(")>
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(mylist)>content( )inline<inline_delimiter($)ident(newlist)>delimiter(")>
+comment(//=> [["0", "2", "3"], 4] [["0", "2", "3"], 4])
+
+comment(// standard deep copy implementation)
+keyword(def) method(deepcopy)operator(()ident(orig)(\)) operator({)
+ ident(bos) operator(=) keyword(new) pre_type(ByteArrayOutputStream)operator(()(\))
+ ident(oos) operator(=) keyword(new) pre_type(ObjectOutputStream)operator(()ident(bos)(\))
+ ident(oos)operator(.)ident(writeObject)operator(()ident(orig)(\))operator(;) ident(oos)operator(.)ident(flush)operator(()(\))
+ ident(bin) operator(=) keyword(new) pre_type(ByteArrayInputStream)operator(()ident(bos)operator(.)ident(toByteArray)operator(()(\)\))
+ ident(ois) operator(=) keyword(new) pre_type(ObjectInputStream)operator(()ident(bin)(\))
+ keyword(return) ident(ois)operator(.)ident(readObject)operator(()(\))
+(})
+
+ident(newlist) operator(=) ident(deepcopy)operator(()ident(oldlist)(\)) comment(// deep copy)
+ident(newmap) operator(=) ident(deepcopy)operator(()ident(oldmap)(\)) comment(// deep copy)
+
+ident(mylist) operator(=) operator([)operator([)string<delimiter(")content(1)delimiter(")>operator(,) string<delimiter(")content(2)delimiter(")>operator(,) string<delimiter(")content(3)delimiter(")>(])operator(,) integer(4)(])
+ident(newlist) operator(=) ident(deepcopy)operator(()ident(mylist)(\))
+ident(mylist)operator([)integer(0)(])operator([)integer(0)(]) operator(=) string<delimiter(")content(0)delimiter(")>
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(mylist)>content( )inline<inline_delimiter($)ident(newlist)>delimiter(")>
+comment(//=> [["0", "2", "3"], 4] [["1", "2", "3"], 4])
+
+comment(// See also:)
+comment(// http://javatechniques.com/public/java/docs/basics/low-memory-deep-copy.html)
+comment(// http://javatechniques.com/public/java/docs/basics/faster-deep-copy.html)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// use Java's serialization capabilities as per 11.10)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.14)
+comment(//----------------------------------------------------------------------------------)
+comment(// There are numerous mechanisms for persisting objects to disk)
+comment(// using Groovy and Java mechanisms. Some are completely transparent,)
+comment(// some require some initialization only, others make the persistence)
+comment(// mechanisms visible. Here is a site that lists over 20 options:)
+comment(// http://www.java-source.net/open-source/persistence)
+comment(// (This list doesn't include EJB offerings which typically)
+comment(// require an application server or XML-based options\))
+
+comment(// We'll just consider one possibility from prevayler.sf.net.)
+comment(// This package doesn't make changes to persistent data transparent;)
+comment(// instead requiring an explicit call via a transaction object.)
+comment(// It saves all such transaction objects in a journal file so)
+comment(// that it can rollback the system any number of times (or if)
+comment(// you make use of the timestamp feature\) to a particular point)
+comment(// in time. It can also be set up to create snapshots which)
+comment(// consolidate all changes made up to a certain point. The)
+comment(// journalling will begin again from that point.)
+keyword(import) include(org.prevayler.*)
+type(class) class(ImportantHash) directive(implements) pre_type(Serializable) operator({)
+ directive(private) ident(map) operator(=) operator([)operator(:)(])
+ keyword(def) method(putAt)operator(()ident(key)operator(,) ident(value)(\)) operator({) ident(map)operator([)ident(key)(]) operator(=) ident(value) (})
+ keyword(def) method(getAt)operator(()ident(key)(\)) operator({) ident(map)operator([)ident(key)(]) (})
+(})
+type(class) class(StoreTransaction) directive(implements) ident(Transaction) operator({)
+ directive(private) ident(val)
+ ident(StoreTransaction)operator(()ident(val)(\)) operator({) local_variable(this)operator(.)ident(val) operator(=) ident(val) (})
+ type(void) ident(executeOn)operator(()ident(prevayler)operator(,) pre_type(Date) ident(ignored)(\)) operator({) ident(prevayler)operator(.)ident(putAt)operator(()ident(val)operator(,)ident(val)operator(*)integer(2)(\)) (})
+(})
+keyword(def) method(save)operator(()ident(n)(\))operator({) ident(store)operator(.)ident(execute)operator(()keyword(new) ident(StoreTransaction)operator(()ident(n)(\)\)) (})
+ident(store) operator(=) ident(PrevaylerFactory)operator(.)ident(createPrevayler)operator(()keyword(new) ident(ImportantHash)operator(()(\))operator(,) string<delimiter(")content(pleac11)delimiter(")>(\))
+ident(hash) operator(=) ident(store)operator(.)ident(prevalentSystem)operator(()(\))
+keyword(for) operator(()ident(i) keyword(in) integer(0)operator(..)integer(1000)(\)) operator({)
+ ident(save)operator(()ident(i)(\))
+(})
+ident(println) ident(hash)operator([)integer(750)(]) comment(// => 1500)
+
+ident(store) operator(=) keyword(null)operator(;) ident(hash) operator(=) keyword(null) comment(// *** could shutdown here)
+
+ident(store) operator(=) ident(PrevaylerFactory)operator(.)ident(createPrevayler)operator(()keyword(new) ident(ImportantHash)operator(()(\))operator(,) string<delimiter(")content(pleac11)delimiter(")>(\))
+ident(hash) operator(=) ident(store)operator(.)ident(prevalentSystem)operator(()(\))
+ident(println) ident(hash)operator([)integer(750)(]) comment(// => 1500)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_11.15)
+comment(//----------------------------------------------------------------------------------)
+comment(// bintree - binary tree demo program)
+type(class) class(BinaryTree) operator({)
+ keyword(def) ident(value)operator(,) ident(left)operator(,) ident(right)
+ ident(BinaryTree)operator(()ident(val)(\)) operator({)
+ ident(value) operator(=) ident(val)
+ ident(left) operator(=) keyword(null)
+ ident(right) operator(=) keyword(null)
+ (})
+
+ comment(// insert given value into proper point of)
+ comment(// provided tree. If no tree provided,)
+ comment(// use implicit pass by reference aspect of @_)
+ comment(// to fill one in for our caller.)
+ keyword(def) method(insert)operator(()ident(val)(\)) operator({)
+ keyword(if) operator(()ident(val) operator(<) ident(value)(\)) operator({)
+ keyword(if) operator(()ident(left)(\)) ident(left)operator(.)ident(insert)operator(()ident(val)(\))
+ keyword(else) ident(left) operator(=) keyword(new) ident(BinaryTree)operator(()ident(val)(\))
+ (}) keyword(else) keyword(if) operator(()ident(val) operator(>) ident(value)(\)) operator({)
+ keyword(if) operator(()ident(right)(\)) ident(right)operator(.)ident(insert)operator(()ident(val)(\))
+ keyword(else) ident(right) operator(=) keyword(new) ident(BinaryTree)operator(()ident(val)(\))
+ (}) keyword(else) ident(println) string<delimiter(")content(double)delimiter(")> comment(// ignore double values)
+ (})
+
+ comment(// recurse on left child,)
+ comment(// then show current value,)
+ comment(// then recurse on right child.)
+ keyword(def) method(inOrder)operator(()(\)) operator({)
+ keyword(if) operator(()ident(left)(\)) ident(left)operator(.)ident(inOrder)operator(()(\))
+ ident(print) ident(value) operator(+) string<delimiter(')content( )delimiter(')>
+ keyword(if) operator(()ident(right)(\)) ident(right)operator(.)ident(inOrder)operator(()(\))
+ (})
+
+ comment(// show current value,)
+ comment(// then recurse on left child,)
+ comment(// then recurse on right child.)
+ keyword(def) method(preOrder)operator(()(\)) operator({)
+ ident(print) ident(value) operator(+) string<delimiter(')content( )delimiter(')>
+ keyword(if) operator(()ident(left)(\)) ident(left)operator(.)ident(preOrder)operator(()(\))
+ keyword(if) operator(()ident(right)(\)) ident(right)operator(.)ident(preOrder)operator(()(\))
+ (})
+
+ comment(// show current value,)
+ comment(// then recurse on left child,)
+ comment(// then recurse on right child.)
+ keyword(def) method(dumpOrder)operator(()(\)) operator({)
+ ident(print) local_variable(this)operator(.)ident(dump)operator(()(\)) operator(+) string<delimiter(')content( )delimiter(')>
+ keyword(if) operator(()ident(left)(\)) ident(left)operator(.)ident(dumpOrder)operator(()(\))
+ keyword(if) operator(()ident(right)(\)) ident(right)operator(.)ident(dumpOrder)operator(()(\))
+ (})
+
+ comment(// recurse on left child,)
+ comment(// then recurse on right child,)
+ comment(// then show current value.)
+ keyword(def) method(postOrder)operator(()(\)) operator({)
+ keyword(if) operator(()ident(left)(\)) ident(left)operator(.)ident(postOrder)operator(()(\))
+ keyword(if) operator(()ident(right)(\)) ident(right)operator(.)ident(postOrder)operator(()(\))
+ ident(print) ident(value) operator(+) string<delimiter(')content( )delimiter(')>
+ (})
+
+ comment(// find out whether provided value is in the tree.)
+ comment(// if so, return the node at which the value was found.)
+ comment(// cut down search time by only looking in the correct)
+ comment(// branch, based on current value.)
+ keyword(def) method(search)operator(()ident(val)(\)) operator({)
+ keyword(if) operator(()ident(val) operator(==) ident(value)(\)) operator({)
+ keyword(return) local_variable(this)operator(.)ident(dump)operator(()(\))
+ (}) keyword(else) keyword(if) operator(()ident(val) operator(<) ident(value)(\)) operator({)
+ keyword(return) ident(left) operator(?) ident(left)operator(.)ident(search)operator(()ident(val)(\)) operator(:) keyword(null)
+ (}) keyword(else) operator({)
+ keyword(return) ident(right) operator(?) ident(right)operator(.)ident(search)operator(()ident(val)(\)) operator(:) keyword(null)
+ (})
+ (})
+(})
+
+comment(// first generate 20 random inserts)
+ident(test) operator(=) keyword(new) ident(BinaryTree)operator(()integer(500)(\))
+ident(rand) operator(=) keyword(new) pre_type(Random)operator(()(\))
+integer(20)operator(.)ident(times)operator({)
+ ident(test)operator(.)ident(insert)operator(()ident(rand)operator(.)ident(nextInt)operator(()integer(1000)(\)\))
+(})
+
+comment(// now dump out the tree all three ways)
+ident(print) string<delimiter(")content(Pre order: )delimiter(")>operator(;) ident(test)operator(.)ident(preOrder)operator(()(\))operator(;) ident(println) string<delimiter(")delimiter(")>
+ident(print) string<delimiter(")content(In order: )delimiter(")>operator(;) ident(test)operator(.)ident(inOrder)operator(()(\))operator(;) ident(println) string<delimiter(")delimiter(")>
+ident(print) string<delimiter(")content(Post order: )delimiter(")>operator(;) ident(test)operator(.)ident(postOrder)operator(()(\))operator(;) ident(println) string<delimiter(")delimiter(")>
+
+ident(println) string<delimiter(")char(\\n)content(Search?)delimiter(")>
+keyword(while) operator(()operator(()ident(item) operator(=) pre_type(System)operator(.)ident(in)operator(.)ident(readLine)operator(()(\))operator(?)operator(.)ident(trim)operator(()(\)\)) operator(!=) keyword(null)(\)) operator({)
+ ident(println) ident(test)operator(.)ident(search)operator(()ident(item)operator(.)ident(toInteger)operator(()(\)\))
+ ident(println) string<delimiter(")char(\\n)content(Search?)delimiter(")>
+(})
+comment(// Randomly produces a tree such as:)
+comment(// -------- 500 ------)
+comment(// / \\
+// 181 847)
+comment(// / \\ / \\
+// 3 204 814 970)
+comment(// \\ / \\ /)
+comment(// 126 196 414 800)
+comment(// / \\ /)
+comment(// 353 438 621)
+comment(// / / \\
+// 423 604 776)
+comment(// / /)
+comment(// 517 765)
+comment(// /)
+comment(// 646)
+comment(// /)
+comment(// 630)
+comment(// Pre order:)
+comment(// 500 181 3 126 204 196 414 353 438 423 847 814 800 621 604 517 776 765 646 630 970)
+comment(// In order:)
+comment(// 3 126 181 196 204 353 414 423 438 500 517 604 621 630 646 765 776 800 814 847 970)
+comment(// Post order:)
+comment(// 126 3 196 353 423 438 414 204 181 517 604 630 646 765 776 621 800 814 970 847 500)
+comment(//)
+comment(// Search?)
+comment(// 125)
+comment(// null)
+comment(//)
+comment(// Search?)
+comment(// 126)
+comment(// <BinaryTree@ae97c4 value=126 left=null right=null>)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.0)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy adopts many of the Java structuring conventions and terminology)
+comment(// and adds some concepts of its own.)
+comment(// Code-reuse can occur at the script, class, library, component or framework level.)
+comment(// Source code including class file source and scripts are organised into packages.)
+comment(// These can be thought of as like hierarchical folders or directories. Two class)
+comment(// with the same name can be distinguished by having different packages. Compiled)
+comment(// byte code and sometimes source code including scripts can be packaged up into)
+comment(// jar files. Various conventions exist for packaging classes and resources in)
+comment(// such a way to allow them to be easily reused. Some of these conventions allow)
+comment(// reusable code to be placed within repositories for easy use by third parties.)
+comment(// One such repository is the maven repository, e.g.: ibiblio.org/maven2)
+comment(// When reusing classes, it is possible to compartmentalise knowledge of)
+comment(// particular packages using multiple (potentially hierarchical\) classloaders.)
+comment(// By convention, package names are all lowercase. Class names are capitalized.)
+comment(// Naming examples:)
+comment(// package my.package1.name // at most one per source file - at top of file)
+comment(// class MyClass ... // actually defines my.package1.name.MyClass)
+comment(// import my.package1.name.MyClass // allows package to be dropped within current file)
+comment(// import my.package2.name.MyClass // if class basenames are the same, can't)
+comment(// // import both, leave one fully qualified)
+comment(// import my.package.name.* // all classes in package can drop package prefix)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.1)
+comment(//----------------------------------------------------------------------------------)
+comment(// No equivalent export process exists for Groovy.)
+
+comment(// If you have some Groovy functionality that you would like others to use)
+comment(// you either make the source code available or compile it into class files)
+comment(// and package those up in a jar file. Some subset of your class files will)
+comment(// define the OO interface to your functionality, e.g. public methods,)
+comment(// interfaces, etc. Depending on the circumstances, various conventions are)
+comment(// used to indicate this functionality including Manifest files, javadocs,)
+comment(// deployment descriptors, project metadata and dependency management files.)
+comment(// See 12.18 for an example.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy supports both static and dynamic (strong\) typing. When trying to)
+comment(// compile or run files using static typing, the required classes referenced)
+comment(// must be available. Classes used in more dynamic ways may be loaded (or)
+comment(// created\) at runtime. Errors in loading such dynamic cases are handled)
+comment(// using the normal exception handling mechanisms.)
+
+comment(// attempt to load an unknown resource or script:)
+keyword(try) operator({)
+ ident(evaluate)operator(()keyword(new) pre_type(File)operator(()string<delimiter(')content(doesnotexist.groovy)delimiter(')>(\)\))
+(}) keyword(catch) operator(()pre_type(Exception) pre_type(FileNotFoundException)(\)) operator({)
+ ident(println) string<delimiter(')content(File not found, skipping ...)delimiter(')>
+(})
+comment(// => File not found, skipping ...)
+
+comment(// attempt to load an unknown class:)
+keyword(try) operator({)
+ pre_type(Class)operator(.)ident(forName)operator(()string<delimiter(')content(org.happytimes.LottoNumberGenerator)delimiter(')>(\))
+(}) keyword(catch) operator(()pre_type(ClassNotFoundException) ident(ex)(\)) operator({)
+ ident(println) string<delimiter(')content(Class not found, skipping ...)delimiter(')>
+(})
+comment(// -> Class not found, skipping ...)
+
+comment(// dynamicallly look for a database driver (slight variation to original cookbook\))
+comment(// Note: this hypothetical example ignores certain issues e.g. different url)
+comment(// formats for configuration when establishing a connection with the driver)
+ident(candidates) operator(=) operator([)
+ string<delimiter(')content(oracle.jdbc.OracleDriver)delimiter(')>operator(,)
+ string<delimiter(')content(com.ibm.db2.jcc.DB2Driver)delimiter(')>operator(,)
+ string<delimiter(')content(com.microsoft.jdbc.sqlserver.SQLServerDriver)delimiter(')>operator(,)
+ string<delimiter(')content(net.sourceforge.jtds.jdbc.Driver)delimiter(')>operator(,)
+ string<delimiter(')content(com.sybase.jdbc3.jdbc.SybDriver)delimiter(')>operator(,)
+ string<delimiter(')content(com.informix.jdbc.IfxDriver)delimiter(')>operator(,)
+ string<delimiter(')content(com.mysql.jdbc.Driver)delimiter(')>operator(,)
+ string<delimiter(')content(org.postgresql.Driver)delimiter(')>operator(,)
+ string<delimiter(')content(com.sap.dbtech.jdbc.DriverSapDB)delimiter(')>operator(,)
+ string<delimiter(')content(org.hsqldb.jdbcDriver)delimiter(')>operator(,)
+ string<delimiter(')content(com.pointbase.jdbc.jdbcUniversalDriver)delimiter(')>operator(,)
+ string<delimiter(')content(org.apache.derby.jdbc.ClientDriver)delimiter(')>operator(,)
+ string<delimiter(')content(com.mckoi.JDBCDriver)delimiter(')>operator(,)
+ string<delimiter(')content(org.firebirdsql.jdbc.FBDriver)delimiter(')>operator(,)
+ string<delimiter(')content(sun.jdbc.odbc.JdbcOdbcDriver)delimiter(')>
+(])
+ident(loaded) operator(=) keyword(null)
+keyword(for) operator(()ident(driver) keyword(in) ident(candidates)(\)) operator({)
+ keyword(try) operator({)
+ ident(loaded) operator(=) pre_type(Class)operator(.)ident(forName)operator(()ident(driver)(\))operator(.)ident(newInstance)operator(()(\))
+ keyword(break)
+ (}) keyword(catch) operator(()pre_type(Exception) ident(ex)(\)) operator({) comment(/* ignore */) (})
+(})
+ident(println) ident(loaded)operator(?)operator(.)ident(class)operator(?)operator(.)ident(name) comment(// => sun.jdbc.odbc.JdbcOdbcDriver)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// In Groovy (like Java\), any static reference to an external class within)
+comment(// your class will cause the external class to be loaded from the classpath.)
+comment(// You can dynamically add to the classpath using:)
+comment(// this.class.rootLoader.addURL(url\))
+comment(// To delay loading of external classes, use Class.forName(\) or evaluate(\))
+comment(// the script separately as shown in 12.2.)
+
+comment(// For the specific case of initialization code, here is another example:)
+comment(// (The code within the anonymous { ... } block is called whenever the)
+comment(// class is loaded.\))
+type(class) class(DbHelper) operator({)
+ keyword(def) method(driver)
+ operator({)
+ keyword(if) operator(()pre_type(System)operator(.)ident(properties)operator(.)string<delimiter(')content(driver)delimiter(')> operator(==) string<delimiter(')content(oracle)delimiter(')>(\))
+ ident(driver) operator(=) pre_type(Class)operator(.)ident(forName)operator(()string<delimiter(')content(oracle.jdbc.OracleDriver)delimiter(')>(\))
+ keyword(else)
+ ident(driver) operator(=) pre_type(Class)operator(.)ident(forName)operator(()string<delimiter(')content(sun.jdbc.odbc.JdbcOdbcDriver)delimiter(')>(\))
+ (})
+(})
+ident(println) keyword(new) ident(DbHelper)operator(()(\))operator(.)ident(driver)operator(.)ident(name) comment(// => sun.jdbc.odbc.JdbcOdbcDriver)
+comment(// call program with -Ddriver=oracle to swap to other driver)
+
+comment(// A slightly related feature: If you want to load a script (typically in a)
+comment(// server environment\) whenever the source file changes, use GroovyScriptEngine(\))
+comment(// instead of GroovyShell(\) when embedding groovy.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// class variables are private unless access functions are defined)
+type(class) class(Alpha) operator({)
+ keyword(def) ident(x) operator(=) integer(10)
+ directive(private) ident(y) operator(=) integer(12)
+(})
+
+ident(println) keyword(new) ident(Alpha)operator(()(\))operator(.)ident(x) comment(// => 10)
+ident(println) keyword(new) ident(Alpha)operator(()(\))operator(.)ident(y) comment(// => 12 when referenced inside source file, error outside)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// You can examine the stacktrace to determine the calling class: see 10.4)
+comment(// When executing a script from a groovy source file, you can either:)
+ident(println) ident(getClass)operator(()(\))operator(.)ident(classLoader)operator(.)ident(resourceLoader)operator(.)ident(loadGroovySource)operator(()ident(getClass)operator(()(\))operator(.)ident(name)(\))
+comment(// => file:/C:/Projects/GroovyExamples/Pleac/classes/pleac12.groovy)
+comment(// or for the initially started script when started using the standard .bat/.sh files)
+ident(println) pre_type(System)operator(.)ident(properties)operator(.)string<delimiter(')content(script.name)delimiter(')>
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// For code which executes at class startup, see the initialization code block)
+comment(// mechanism mentioned in 12.3. For code which should execute during shutdown)
+comment(// see the finalize(\) method discussed (including limitations\) in 13.2.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.7)
+comment(//----------------------------------------------------------------------------------)
+comment(// Each JVM process may have its own classpath (and indeed its own version of Java)
+comment(// runtime and libraries\). You "simply" supply a classpath pointing to different)
+comment(// locations to obtain different modules.)
+comment(// Groovy augments the JVM behaviour by allowing individuals to have a ~/.groovy/lib)
+comment(// directory with additional libraries (and potentially other resources\).)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.8)
+comment(//----------------------------------------------------------------------------------)
+comment(// To make your code available to others could involve any of the following:)
+comment(// (1\) make your source code available)
+comment(// (2\) if you are creating a standard class, use the jar tool to package the)
+comment(// compiled code into a jar - this is then added to the classpath to use)
+comment(// (3\) if the jar relies on additional jars, this is sometimes specified in)
+comment(// a special manifest file within the jar)
+comment(// (4\) if the code is designed to run within a container environment, there)
+comment(// might be additional packaging, e.g. servlets might be packaged in a war)
+comment(// file - essentially a jar file with extra metadata in xml format.)
+comment(// (5\) you might also supply your package to a well known repository such as the)
+comment(// maven repository - and you will add dependency information in xml format)
+comment(// (6\) you may use platform specific installers to produce easily installable)
+comment(// components (e.g. windows .exe files or linux rpm's\))
+comment(// (7\) you may spackage up your components as a plugin (e.g. as an eclipse plugin\))
+comment(// this is also typically in jar/zip like format with additional metadata)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.9)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy has no SelfLoader. Class loading can be delayed using external scripts)
+comment(// and by using the Class.forName(\) approach discussed in 12.2/12.3. If you have)
+comment(// critical performance issues, you can use these techniques and keep your class)
+comment(// size small to maximise the ability to defer loading. There are other kinds of)
+comment(// performance tradeoffs you can make too. Alot of work has been done with JIT)
+comment(// (just in time\) compilers for bytecode. You can pre-compile Groovy source files)
+comment(// into bytecode using the groovy compiler (groovyc\). You can also do this on)
+comment(// the fly for scripts you know you are going to need shortly.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.10)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy has no AutoLoader. See the discussion in 12.9 for some techniques to)
+comment(// impact program performance. There are many techniques available to speed up)
+comment(// program performance (and in particular load speed\). Some of these utilise)
+comment(// techniques similar in nature to the technique used by the AutoLoader.)
+comment(// As already mentioned, when you load a class into the JVM, any statically)
+comment(// referenced class is also loaded. If you reference interfaces rather than)
+comment(// concrete implementations, only the interface need be loaded. If you must)
+comment(// reference a concrete implementation you can use either a Proxy class or)
+comment(// classloader tricks to delay the loading of a full class (e.g. you supply a)
+comment(// Proxy class with just one method implemented or a lazy-loading Proxy which)
+comment(// loads the real class only when absolutely required\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// You can use Categories to override Groovy and Java base functionality.)
+ident(println) keyword(new) pre_type(Date)operator(()(\))operator(.)ident(time) comment(// => 1169019557140)
+
+type(class) class(DateCategory) operator({) comment(// the class name by convention ends with category)
+ comment(// we can add new functionality)
+ directive(static) type(float) ident(getFloatTime)operator(()pre_type(Date) ident(self)(\)) operator({)
+ keyword(return) operator(()type(float)(\)) ident(self)operator(.)ident(getTime)operator(()(\))
+ (})
+ comment(// we can override existing functionality (now seconds since 1970 not millis\))
+ directive(static) type(long) ident(asSeconds)operator(()pre_type(Date) ident(self)(\)) operator({)
+ keyword(return) operator(()type(long)(\)) operator(()ident(self)operator(.)ident(getTime)operator(()(\))operator(/)integer(1000)(\))
+ (})
+(})
+
+ident(use) operator(()ident(DateCategory)(\)) operator({)
+ ident(println) keyword(new) pre_type(Date)operator(()(\))operator(.)ident(floatTime) comment(// => 1.1690195E12)
+ ident(println) keyword(new) pre_type(Date)operator(()(\))operator(.)ident(asSeconds)operator(()(\)) comment(// => 1169019557)
+(})
+
+comment(// We can also use the 'as' keyword)
+type(class) class(MathLib) operator({)
+ keyword(def) method(triple)operator(()ident(n)(\)) operator({) ident(n) operator(*) integer(4) (})
+ keyword(def) method(twice)operator(()ident(n)(\)) operator({) ident(n) operator(*) integer(2) (})
+(})
+keyword(def) ident(m) operator(=) keyword(new) ident(MathLib)operator(()(\))
+ident(println) ident(m)operator(.)ident(twice)operator(()integer(10)(\)) comment(// => 20)
+ident(println) ident(m)operator(.)ident(triple)operator(()integer(10)(\)) comment(// => 40 (Intentional Bug!\))
+comment(// we might want to make use of some funtionality in the math)
+comment(// library but want to later some of its features slightly or fix)
+comment(// some bugs, we can simply import the original using a different name)
+keyword(import) include(MathLib) keyword(as) class(BuggyMathLib)
+comment(// now we could define our own MathLib which extended or had a delegate)
+comment(// of the BuggyMathLib class)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.12)
+comment(//----------------------------------------------------------------------------------)
+comment(// Many Java and Groovy programs emit a stacktrace when an error occurs.)
+comment(// This shows both the calling and called programs (with line numbers if)
+comment(// supplied\). Groovy pretties up stacktraces to show less noise. You can use -d)
+comment(// or --debug on the commandline to force it to always produce full stacktraces.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// already have log10, how to create log11 to log100)
+operator(()integer(11)operator(..)integer(100)(\))operator(.)ident(each) operator({) type(int) ident(base) operator(->)
+ ident(binding)operator(.)string<delimiter(")content(log)inline<inline_delimiter($)ident(base)>delimiter(")> operator(=) operator({) type(int) ident(n) operator(->) pre_type(Math)operator(.)ident(log)operator(()ident(n)(\)) operator(/) pre_type(Math)operator(.)ident(log)operator(()ident(base)(\)) (})
+(})
+ident(println) ident(log20)operator(()integer(400)(\)) comment(// => 2.0)
+ident(println) ident(log100)operator(()integer(1000000)(\)) comment(// => 3.0 (displays 2.9999999999999996 using doubles\))
+
+comment(// same thing again use currying)
+keyword(def) ident(logAnyBase) operator(=) operator({) ident(base)operator(,) ident(n) operator(->) pre_type(Math)operator(.)ident(log)operator(()ident(n)(\)) operator(/) pre_type(Math)operator(.)ident(log)operator(()ident(base)(\)) (})
+operator(()integer(11)operator(..)integer(100)(\))operator(.)ident(each) operator({) type(int) ident(base) operator(->)
+ ident(binding)operator(.)string<delimiter(")content(log)inline<inline_delimiter($)ident(base)>delimiter(")> operator(=) ident(logAnyBase)operator(.)ident(curry)operator(()ident(base)(\))
+(})
+ident(println) ident(log20)operator(()integer(400)(\)) comment(// => 2.0)
+ident(println) ident(log100)operator(()integer(1000000)(\)) comment(// => 3.0 (displays 2.9999999999999996 using doubles\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.14)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy intefaces with C in the same way as Java: using JNI)
+comment(// For this discussion we will ignoring platform specific options and CORBA.)
+comment(// This tutorial here describes how to allow Java (and hence Groovy\) to)
+comment(// call a C program which generates UUIDs:)
+comment(// http://ringlord.com/publications/jni-howto/)
+comment(// Here's another useful reference:)
+comment(// http://weblogs.java.net/blog/kellyohair/archive/2006/01/compilation_of_1.html)
+comment(// And of course, Sun's tutorial:)
+comment(// http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jni.html)
+
+comment(// You might also want to consider SWIG which simplifies connecting)
+comment(// C/C++ to many scripting languages including Java (and hence Groovy\))
+comment(// More details: http://www.swig.org/)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.15)
+comment(//----------------------------------------------------------------------------------)
+comment(// See discussion for 12.14)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.16)
+comment(//----------------------------------------------------------------------------------)
+comment(// The standard documentation system for Java is JavaDoc.)
+comment(// Documentation for JavaDoc is part of a Java installation.)
+comment(// Groovy has a GroovyDoc tool planned which expands upon the JavaDoc tool)
+comment(// but work on the tool hasn't progressed much as yet.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.17)
+comment(//----------------------------------------------------------------------------------)
+comment(// Most libraries for Java (and hence Groovy\) come precompiled. You simply download)
+comment(// the jar and place it somewhere on your CLASSPATH.)
+
+comment(// If only source code is available, you need to download the source and follow any)
+comment(// instuctions which came with the source. Most projects use one of a handful of)
+comment(// build tools to compile, test and package their artifacts. Typical ones are Ant)
+comment(// and Maven which you need to install according to their respective instructions.)
+
+comment(// If using Ant, you need to unpack the source files then type 'ant'.)
+
+comment(// If using Maven, you need to unpack the source files then type 'maven'.)
+
+comment(// If you are using Maven or Ivy for dependency management you can add)
+comment(// the following lines to your project description file.)
+comment(/*
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <version>3.2</version>
+ </dependency>
+*/)
+comment(// This will automatically download the particular version of the referenced)
+comment(// library file and also provide hooks so that you can make this automatically)
+comment(// available in your classpath.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.18)
+comment(//----------------------------------------------------------------------------------)
+comment(// example groovy file for a "module")
+keyword(import) include(org.apache.commons.lang.WordUtils)
+
+type(class) class(Greeter) operator({)
+ keyword(def) ident(name)
+ ident(Greeter)operator(()ident(who)(\)) operator({) ident(name) operator(=) ident(WordUtils)operator(.)ident(capitalize)operator(()ident(who)(\)) (})
+ keyword(def) method(salute)operator(()(\)) operator({) string<delimiter(")content(Hello )inline<inline_delimiter($)ident(name)>content(!)delimiter(")> (})
+(})
+
+comment(// test class)
+type(class) class(GreeterTest) directive(extends) ident(GroovyTestCase) operator({)
+ keyword(def) method(testGreeting)operator(()(\)) operator({)
+ keyword(assert) keyword(new) ident(Greeter)operator(()string<delimiter(')content(world)delimiter(')>(\))operator(.)ident(salute)operator(()(\))
+ (})
+(})
+
+comment(// Typical Ant build file (could be in Groovy instead of XML\):)
+comment(/*
+<?xml version="1.0"?>
+<project name="sample" default="jar" basedir=".">
+ <property name="src" value="src"/>
+ <property name="build" value="build"/>
+
+ <target name="init">
+ <mkdir dir="${build}"/>
+ </target>
+
+ <target name="compile" depends="init">
+ <mkdir dir="${build}/classes"/>
+ <groovyc srcdir="${src}" destdir="${build}/classes"/>
+ </target>
+
+ <target name="test" depends="compile">
+ <groovy src="${src}/GreeterTest.groovy">
+ </target>
+
+ <target name="jar" depends="compile,test">
+ <mkdir dir="${build}/jar"/>
+ <jar destfile="${build}/jar/Greeter.jar" basedir="${build}/classes">
+ <manifest>
+ <attribute name="Main-Class" value="Greeter"/>
+ </manifest>
+ </jar>
+ </target>
+</project>
+
+*/)
+
+comment(// Typical dependency management file)
+comment(/*
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>groovy</groupId>
+ <artifactId>module</artifactId>
+ <name>Greeter</name>
+ <version>1.0</version>
+ <packaging>jar</packaging>
+ <description>Greeter Module/description>
+ <dependencies>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.2</version>
+ </dependency>
+ </dependencies>
+</project>
+*/)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_12.19)
+comment(//----------------------------------------------------------------------------------)
+comment(// Searching available modules in repositories:)
+comment(// You can browse the repositories online, e.g. ibiblio.org/maven2 or various)
+comment(// plugins are available for IDEs which do this for you, e.g. JarJuggler for IntelliJ.)
+
+comment(// Searching currently "installed" modules:)
+comment(// Browse your install directory, view your maven POM file, look in your ~/.groovy/lib)
+comment(// directory, turn on debug modes and watch classloader messages ...)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.0)
+comment(//----------------------------------------------------------------------------------)
+comment(// Classes and objects in Groovy are rather straigthforward)
+type(class) class(Person) operator({)
+ comment(// Class variables (also called static attributes\) are prefixed by the keyword static)
+ directive(static) ident(personCounter)operator(=)integer(0)
+ keyword(def) ident(age)operator(,) ident(name) comment(// this creates setter and getter methods)
+ directive(private) ident(alive)
+
+ comment(// object constructor)
+ ident(Person)operator(()ident(age)operator(,) ident(name)operator(,) ident(alive) operator(=) keyword(true)(\)) operator({) comment(// Default arg like in C++)
+ local_variable(this)operator(.)ident(age) operator(=) ident(age)
+ local_variable(this)operator(.)ident(name) operator(=) ident(name)
+ local_variable(this)operator(.)ident(alive) operator(=) ident(alive)
+ ident(personCounter) operator(+=) integer(1)
+ comment(// There is a '++' operator in Groovy but using += is often clearer.)
+ (})
+
+ keyword(def) method(die)operator(()(\)) operator({)
+ ident(alive) operator(=) keyword(false)
+ ident(println) string<delimiter(")inline<inline_delimiter($)ident(name)>content( has died at the age of )inline<inline_delimiter($)ident(age)>content(.)delimiter(")>
+ ident(alive)
+ (})
+
+ keyword(def) method(kill)operator(()ident(anotherPerson)(\)) operator({)
+ ident(println) string<delimiter(")inline<inline_delimiter($)ident(name)>content( is killing )inline<inline_delimiter($)ident(anotherPerson)>content(.name.)delimiter(")>
+ ident(anotherPerson)operator(.)ident(die)operator(()(\))
+ (})
+
+ comment(// methods used as queries generally start with is, are, will or can)
+ comment(// usually have the '?' suffix)
+ keyword(def) method(isStillAlive)operator(()(\)) operator({)
+ ident(alive)
+ (})
+
+ keyword(def) method(getYearOfBirth)operator(()(\)) operator({)
+ keyword(new) pre_type(Date)operator(()(\))operator(.)ident(year) operator(-) ident(age)
+ (})
+
+ comment(// Class method (also called static method\))
+ directive(static) ident(getNumberOfPeople)operator(()(\)) operator({) comment(// accessors often start with get)
+ comment(// in which case you can call it like)
+ comment(// it was a field (without the get\))
+ ident(personCounter)
+ (})
+(})
+
+comment(// Using the class:)
+comment(// Create objects of class Person)
+ident(lecter) operator(=) keyword(new) ident(Person)operator(()integer(47)operator(,) string<delimiter(')content(Hannibal)delimiter(')>(\))
+ident(starling) operator(=) keyword(new) ident(Person)operator(()integer(29)operator(,) string<delimiter(')content(Clarice)delimiter(')>operator(,) keyword(true)(\))
+ident(pazzi) operator(=) keyword(new) ident(Person)operator(()integer(40)operator(,) string<delimiter(')content(Rinaldo)delimiter(')>operator(,) keyword(true)(\))
+
+comment(// Calling a class method)
+ident(println) string<delimiter(")content(There are )inline<inline_delimiter($)ident(Person)>content(.numberOfPeople Person objects.)delimiter(")>
+
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(pazzi)>content(.name is )inline<inline_delimiter(${)ident(pazzi.alive ? 'alive' : 'dead')inline_delimiter(})>content(.)delimiter(")>
+ident(lecter)operator(.)ident(kill)operator(()ident(pazzi)(\))
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(pazzi)>content(.name is )inline<inline_delimiter(${)ident(pazzi.isStillAlive(\) ? 'alive' : 'dead')inline_delimiter(})>content(.)delimiter(")>
+
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(starling)>content(.name was born in )inline<inline_delimiter($)ident(starling)>content(.yearOfBirth.)delimiter(")>
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.1)
+comment(//----------------------------------------------------------------------------------)
+comment(// Classes may have no constructor.)
+type(class) class(MyClass) operator({) (})
+
+ident(aValidButNotVeryUsefulObject) operator(=) keyword(new) ident(MyClass)operator(()(\))
+
+comment(// If no explicit constructor is given a default implicit)
+comment(// one which supports named parameters is provided.)
+type(class) class(MyClass2) operator({)
+ keyword(def) ident(start) operator(=) keyword(new) pre_type(Date)operator(()(\))
+ keyword(def) ident(age) operator(=) integer(0)
+(})
+ident(println) keyword(new) ident(MyClass2)operator(()key(age)operator(:)integer(4)(\))operator(.)ident(age) comment(// => 4)
+
+comment(// One or more explicit constructors may also be provided)
+type(class) class(MyClass3) operator({)
+ keyword(def) ident(start)
+ keyword(def) ident(age)
+ ident(MyClass3)operator(()ident(date)operator(,) ident(age)(\)) operator({)
+ ident(start) operator(=) ident(date)
+ local_variable(this)operator(.)ident(age) operator(=) ident(age)
+ (})
+(})
+ident(println) keyword(new) ident(MyClass3)operator(()keyword(new) pre_type(Date)operator(()(\))operator(,) integer(20)(\))operator(.)ident(age) comment(// => 20)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// Objects are destroyed by the JVM garbage collector.)
+comment(// The time of destroying is not predicated but left up to the JVM.)
+comment(// There is no direct support for destructor. There is a courtesy)
+comment(// method called finalize(\) which the JVM may call when disposing)
+comment(// an object. If you need to free resources for an object, like)
+comment(// closing a socket or killing a spawned subprocess, you should do)
+comment(// it explicitly - perhaps by supporting your own lifecycle methods)
+comment(// on your class, e.g. close(\).)
+
+type(class) class(MyClass4)operator({)
+ type(void) ident(finalize)operator(()(\)) operator({)
+ ident(println) string<delimiter(")content(Object [internal id=)inline<inline_delimiter(${)ident(hashCode(\))inline_delimiter(})>content(] is dying at )inline<inline_delimiter(${)ident(new Date(\))inline_delimiter(})>delimiter(")>
+ (})
+(})
+
+comment(// test code)
+integer(50)operator(.)ident(times) operator({)
+ keyword(new) ident(MyClass4)operator(()(\))
+(})
+integer(20)operator(.)ident(times) operator({)
+ pre_type(System)operator(.)ident(gc)operator(()(\))
+(})
+comment(// => (between 0 and 50 lines similar to below\))
+comment(// Object [internal id=10884088] is dying at Wed Jan 10 16:33:33 EST 2007)
+comment(// Object [internal id=6131844] is dying at Wed Jan 10 16:33:33 EST 2007)
+comment(// Object [internal id=12245160] is dying at Wed Jan 10 16:33:33 EST 2007)
+comment(// ...)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// You can write getter and setter methods explicitly as shown below.)
+comment(// One convention is to use set and get at the start of method names.)
+type(class) class(Person2) operator({)
+ directive(private) ident(name)
+ keyword(def) method(getName)operator(()(\)) operator({) ident(name) (})
+ keyword(def) method(setName)operator(()ident(name)(\)) operator({) local_variable(this)operator(.)ident(name) operator(=) ident(name) (})
+(})
+
+comment(// You can also just use def which auto defines default getters and setters.)
+type(class) class(Person3) operator({)
+ keyword(def) ident(age)operator(,) ident(name)
+(})
+
+comment(// Any variables marked as final will only have a default getter.)
+comment(// You can also write an explicit getter. For a write-only variable)
+comment(// just write only a setter.)
+type(class) class(Person4) operator({)
+ directive(final) ident(age) comment(// getter only)
+ keyword(def) ident(name) comment(// getter and setter)
+ directive(private) ident(color) comment(// private)
+ keyword(def) method(setColor)operator(()(\)) operator({) local_variable(this)operator(.)ident(color) operator(=) ident(color) (}) comment(// setter only)
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.4)
+comment(//----------------------------------------------------------------------------------)
+type(class) class(Person5) operator({)
+ comment(// Class variables (also called static attributes\) are prefixed by the keyword static)
+ directive(static) ident(personCounter) operator(=) integer(0)
+
+ directive(static) ident(getPopulation)operator(()(\)) operator({)
+ ident(personCounter)
+ (})
+ ident(Person5)operator(()(\)) operator({)
+ ident(personCounter) operator(+=) integer(1)
+ (})
+ type(void) ident(finalize)operator(()(\)) operator({)
+ ident(personCounter) operator(-=) integer(1)
+ (})
+(})
+ident(people) operator(=) type([])
+integer(10)operator(.)ident(times) operator({)
+ ident(people) operator(+=) keyword(new) ident(Person5)operator(()(\))
+(})
+ident(println) string<delimiter(")content(There are )inline<inline_delimiter(${)ident(Person5.population)inline_delimiter(})>content( people alive)delimiter(")>
+comment(// => There are 10 people alive)
+
+ident(alpha) operator(=) keyword(new) ident(FixedArray)operator(()(\))
+ident(println) string<delimiter(")content(Bound on alpha is )inline<inline_delimiter($)ident(alpha)>content(.maxBounds)delimiter(")>
+
+ident(beta) operator(=) keyword(new) ident(FixedArray)operator(()(\))
+ident(beta)operator(.)ident(maxBounds) operator(=) integer(50)
+ident(println) string<delimiter(")content(Bound on alpha is )inline<inline_delimiter($)ident(alpha)>content(.maxBounds)delimiter(")>
+
+type(class) class(FixedArray) operator({)
+ directive(static) ident(maxBounds) operator(=) integer(100)
+
+ keyword(def) method(getMaxBounds)operator(()(\)) operator({)
+ ident(maxBounds)
+ (})
+ keyword(def) method(setMaxBounds)operator(()ident(value)(\)) operator({)
+ ident(maxBounds) operator(=) ident(value)
+ (})
+(})
+comment(// =>)
+comment(// Bound on alpha is 100)
+comment(// Bound on alpha is 50)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// The fields of this struct-like class are dynamically typed)
+type(class) class(DynamicPerson) operator({) keyword(def) ident(name)operator(,) ident(age)operator(,) ident(peers) (})
+ident(p) operator(=) keyword(new) ident(DynamicPerson)operator(()(\))
+ident(p)operator(.)ident(name) operator(=) string<delimiter(")content(Jason Smythe)delimiter(")>
+ident(p)operator(.)ident(age) operator(=) integer(13)
+ident(p)operator(.)ident(peers) operator(=) operator([)string<delimiter(")content(Wilbur)delimiter(")>operator(,) string<delimiter(")content(Ralph)delimiter(")>operator(,) string<delimiter(")content(Fred)delimiter(")>(])
+ident(p)operator(.)ident(setPeers)operator(()operator([)string<delimiter(")content(Wilbur)delimiter(")>operator(,) string<delimiter(")content(Ralph)delimiter(")>operator(,) string<delimiter(")content(Fred)delimiter(")>(]\)) comment(// alternative using implicit setter)
+ident(p)operator([)string<delimiter(")content(peers)delimiter(")>(]) operator(=) operator([)string<delimiter(")content(Wilbur)delimiter(")>operator(,) string<delimiter(")content(Ralph)delimiter(")>operator(,) string<delimiter(")content(Fred)delimiter(")>(]) comment(// alternative access using name of field)
+ident(println) string<delimiter(")content(At age )inline<inline_delimiter($)ident(p)>content(.age, )inline<inline_delimiter($)ident(p)>content(.name's first friend is )inline<inline_delimiter(${)ident(p.peers[0])inline_delimiter(})>delimiter(")>
+comment(// => At age 13, Jason Smythe's first friend is Wilbur)
+
+comment(// The fields of this struct-like class are statically typed)
+type(class) class(StaticPerson) operator({) pre_type(String) ident(name)operator(;) type(int) ident(age)operator(;) pre_type(List) ident(peers) (})
+ident(p) operator(=) keyword(new) ident(StaticPerson)operator(()key(name)operator(:)string<delimiter(')content(Jason)delimiter(')>operator(,) key(age)operator(:)integer(14)operator(,) key(peers)operator(:)operator([)string<delimiter(')content(Fred)delimiter(')>operator(,)string<delimiter(')content(Wilbur)delimiter(')>operator(,)string<delimiter(')content(Ralph)delimiter(')>(]\))
+ident(println) string<delimiter(")content(At age )inline<inline_delimiter($)ident(p)>content(.age, )inline<inline_delimiter($)ident(p)>content(.name's first friend is )inline<inline_delimiter(${)ident(p.peers[0])inline_delimiter(})>delimiter(")>
+comment(// => At age 14, Jason's first friend is Fred)
+
+
+type(class) class(Family) operator({) keyword(def) ident(head)operator(,) ident(address)operator(,) ident(members) (})
+ident(folks) operator(=) keyword(new) ident(Family)operator(()key(head)operator(:)keyword(new) ident(DynamicPerson)operator(()key(name)operator(:)string<delimiter(')content(John)delimiter(')>operator(,)key(age)operator(:)integer(34)(\)\))
+
+comment(// supply of own accessor method for the struct for error checking)
+type(class) class(ValidatingPerson) operator({)
+ directive(private) ident(age)
+ keyword(def) method(printAge)operator(()(\)) operator({) ident(println) string<delimiter(')content(Age=)delimiter(')> operator(+) ident(age) (})
+ keyword(def) method(setAge)operator(()ident(value)(\)) operator({)
+ keyword(if) operator(()operator(!)operator(()ident(value) keyword(instanceof) pre_type(Integer)(\)\))
+ keyword(throw) keyword(new) pre_type(IllegalArgumentException)operator(()string<delimiter(")content(Argument ')inline<inline_delimiter(${)ident(value)inline_delimiter(})>content(' isn't an Integer)delimiter(")>(\))
+ keyword(if) operator(()ident(value) operator(>) integer(150)(\))
+ keyword(throw) keyword(new) pre_type(IllegalArgumentException)operator(()string<delimiter(")content(Age )inline<inline_delimiter(${)ident(value)inline_delimiter(})>content( is unreasonable)delimiter(")>(\))
+ ident(age) operator(=) ident(value)
+ (})
+(})
+
+comment(// test ValidatingPerson)
+keyword(def) method(tryCreate)operator(()ident(arg)(\)) operator({)
+ keyword(try) operator({)
+ keyword(new) ident(ValidatingPerson)operator(()key(age)operator(:)ident(arg)(\))operator(.)ident(printAge)operator(()(\))
+ (}) keyword(catch) operator(()pre_type(Exception) ident(ex)(\)) operator({)
+ ident(println) ident(ex)operator(.)ident(message)
+ (})
+(})
+
+ident(tryCreate)operator(()integer(20)(\))
+ident(tryCreate)operator(()string<delimiter(')content(Youngish)delimiter(')>(\))
+ident(tryCreate)operator(()integer(200)(\))
+comment(// =>)
+comment(// Age=20)
+comment(// Argument 'Youngish' isn't an Integer)
+comment(// Age 200 is unreasonable)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy objects are (loosely speaking\) extended Java objects.)
+comment(// Java's Object class provides a clone(\) method. The conventions of)
+comment(// clone(\) are that if I say a = b.clone(\) then a and b should be)
+comment(// different objects with the same type and value. Java doesn't)
+comment(// enforce a class to implement a clone(\) method at all let alone)
+comment(// require that one has to meet these conventions. Classes which)
+comment(// do support clone(\) should implement the Cloneable interface and)
+comment(// implement an equals(\) method.)
+comment(// Groovy follows Java's conventions for clone(\).)
+
+type(class) class(A) directive(implements) pre_type(Cloneable) operator({)
+ keyword(def) ident(name)
+ type(boolean) ident(equals)operator(()pre_type(Object) ident(other)(\)) operator({)
+ ident(other) keyword(instanceof) ident(A) operator(&&) local_variable(this)operator(.)ident(name) operator(==) ident(other)operator(.)ident(name)
+ (})
+(})
+ident(ob1) operator(=) keyword(new) ident(A)operator(()key(name)operator(:)string<delimiter(')content(My named thing)delimiter(')>(\))
+
+ident(ob2) operator(=) ident(ob1)operator(.)ident(clone)operator(()(\))
+keyword(assert) operator(!)ident(ob1)operator(.)ident(is)operator(()ident(ob2)(\))
+keyword(assert) ident(ob1)operator(.)ident(class) operator(==) ident(ob2)operator(.)ident(class)
+keyword(assert) ident(ob2)operator(.)ident(name) operator(==) ident(ob1)operator(.)ident(name)
+keyword(assert) ident(ob1) operator(==) ident(ob2)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.7)
+comment(//----------------------------------------------------------------------------------)
+type(class) class(CanFlicker) operator({)
+ keyword(def) method(flicker)operator(()ident(arg)(\)) operator({) keyword(return) ident(arg) operator(*) integer(2) (})
+(})
+ident(methname) operator(=) string<delimiter(')content(flicker)delimiter(')>
+keyword(assert) keyword(new) ident(CanFlicker)operator(()(\))operator(.)ident(invokeMethod)operator(()ident(methname)operator(,) integer(10)(\)) operator(==) integer(20)
+keyword(assert) keyword(new) ident(CanFlicker)operator(()(\))operator(.)string<delimiter(")inline<inline_delimiter($)ident(methname)>delimiter(")>operator(()integer(10)(\)) operator(==) integer(20)
+
+type(class) class(NumberEcho) operator({)
+ keyword(def) method(one)operator(()(\)) operator({) integer(1) (})
+ keyword(def) method(two)operator(()(\)) operator({) integer(2) (})
+ keyword(def) method(three)operator(()(\)) operator({) integer(3) (})
+(})
+ident(obj) operator(=) keyword(new) ident(NumberEcho)operator(()(\))
+comment(// call methods on the object, by name)
+keyword(assert) operator([)string<delimiter(')content(one)delimiter(')>operator(,) string<delimiter(')content(two)delimiter(')>operator(,) string<delimiter(')content(three)delimiter(')>operator(,) string<delimiter(')content(two)delimiter(')>operator(,) string<delimiter(')content(one)delimiter(')>(])operator(.)ident(collect)operator({) ident(obj)operator(.)string<delimiter(")inline<inline_delimiter($)local_variable(it)>delimiter(")>operator(()(\)) (})operator(.)ident(join)operator(()(\)) operator(==) string<delimiter(')content(12321)delimiter(')>
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.8)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy can work with Groovy objects which inherit from a common base)
+comment(// class called GroovyObject or Java objects which inherit from Object.)
+
+comment(// the class of the object)
+keyword(assert) string<delimiter(')content(a string)delimiter(')>operator(.)ident(class) operator(==) ident(java)operator(.)ident(lang)operator(.)ident(String)
+
+comment(// Groovy classes are actually objects of class Class and they)
+comment(// respond to methods defined in the Class class as well)
+keyword(assert) string<delimiter(')content(a string)delimiter(')>operator(.)ident(class)operator(.)ident(class) operator(==) ident(java)operator(.)ident(lang)operator(.)ident(Class)
+keyword(assert) operator(!)string<delimiter(')content(a string)delimiter(')>operator(.)ident(class)operator(.)ident(isArray)operator(()(\))
+
+comment(// ask an object whether it is an instance of particular class)
+ident(n) operator(=) float(4.7f)
+ident(println) operator(()ident(n) keyword(instanceof) pre_type(Integer)(\)) comment(// false)
+ident(println) operator(()ident(n) keyword(instanceof) pre_type(Float)(\)) comment(// true)
+ident(println) operator(()ident(n) keyword(instanceof) pre_type(Double)(\)) comment(// false)
+ident(println) operator(()ident(n) keyword(instanceof) pre_type(String)(\)) comment(// false)
+ident(println) operator(()ident(n) keyword(instanceof) ident(StaticPerson)(\)) comment(// false)
+
+comment(// ask if a class or interface is either the same as, or is a)
+comment(// superclass or superinterface of another class)
+ident(println) ident(n)operator(.)ident(class)operator(.)ident(isAssignableFrom)operator(()pre_type(Float)operator(.)ident(class)(\)) comment(// true)
+ident(println) ident(n)operator(.)ident(class)operator(.)ident(isAssignableFrom)operator(()pre_type(String)operator(.)ident(class)(\)) comment(// false)
+
+comment(// can a Groovy object respond to a particular method?)
+keyword(assert) keyword(new) ident(CanFlicker)operator(()(\))operator(.)ident(metaClass)operator(.)ident(methods)operator(*.)ident(name)operator(.)ident(contains)operator(()string<delimiter(')content(flicker)delimiter(')>(\))
+
+type(class) class(POGO)operator({)(})
+ident(println) operator(()ident(obj)operator(.)ident(metaClass)operator(.)ident(methods)operator(*.)ident(name) operator(-) keyword(new) ident(POGO)operator(()(\))operator(.)ident(metaClass)operator(.)ident(methods)operator(*.)ident(name)(\))
+comment(// => ["one", "two", "three"])
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.9)
+comment(//----------------------------------------------------------------------------------)
+comment(// Most classes in Groovy are inheritable)
+type(class) class(Person6)operator({) keyword(def) ident(age)operator(,) ident(name) (})
+ident(dude) operator(=) keyword(new) ident(Person6)operator(()key(name)operator(:)string<delimiter(')content(Jason)delimiter(')>operator(,) key(age)operator(:)integer(23)(\))
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(dude)>content(.name is age )inline<inline_delimiter($)ident(dude)>content(.age.)delimiter(")>
+
+comment(// Inheriting from Person)
+type(class) class(Employee) directive(extends) ident(Person6) operator({)
+ keyword(def) ident(salary)
+(})
+ident(empl) operator(=) keyword(new) ident(Employee)operator(()key(name)operator(:)string<delimiter(')content(Jason)delimiter(')>operator(,) key(age)operator(:)integer(23)operator(,) key(salary)operator(:)integer(200)(\))
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(empl)>content(.name is age )inline<inline_delimiter($)ident(empl)>content(.age and has salary )inline<inline_delimiter($)ident(empl)>content(.salary.)delimiter(")>
+
+comment(// Many built-in class can be inherited the same way)
+type(class) class(WierdList) directive(extends) pre_type(ArrayList) operator({)
+ keyword(def) method(size)operator(()(\)) operator({) comment(// size method in this class is overridden)
+ local_variable(super)operator(.)ident(size)operator(()(\)) operator(*) integer(2)
+ (})
+(})
+ident(a) operator(=) keyword(new) ident(WierdList)operator(()(\))
+ident(a)operator(.)ident(add)operator(()string<delimiter(')content(dog)delimiter(')>(\))
+ident(a)operator(.)ident(add)operator(()string<delimiter(')content(cat)delimiter(')>(\))
+ident(println) ident(a)operator(.)ident(size)operator(()(\)) comment(// => 4)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.10)
+comment(//----------------------------------------------------------------------------------)
+type(class) class(Person7) operator({) keyword(def) ident(firstname)operator(,) ident(surname)operator(;) keyword(def) method(getName)operator(()(\))operator({) ident(firstname) operator(+) string<delimiter(')content( )delimiter(')> operator(+) ident(surname) (}) (})
+type(class) class(Employee2) directive(extends) ident(Person7) operator({)
+ keyword(def) ident(employeeId)
+ keyword(def) method(getName)operator(()(\))operator({) string<delimiter(')content(Employee Number )delimiter(')> operator(+) ident(employeeId) (})
+ keyword(def) method(getRealName)operator(()(\))operator({) local_variable(super)operator(.)ident(getName)operator(()(\)) (})
+(})
+ident(p) operator(=) keyword(new) ident(Person7)operator(()key(firstname)operator(:)string<delimiter(')content(Jason)delimiter(')>operator(,) key(surname)operator(:)string<delimiter(')content(Smythe)delimiter(')>(\))
+ident(println) ident(p)operator(.)ident(name)
+comment(// =>)
+comment(// Jason Smythe)
+ident(e) operator(=) keyword(new) ident(Employee2)operator(()key(firstname)operator(:)string<delimiter(')content(Jason)delimiter(')>operator(,) key(surname)operator(:)string<delimiter(')content(Smythe)delimiter(')>operator(,) key(employeeId)operator(:)integer(12349876)(\))
+ident(println) ident(e)operator(.)ident(name)
+ident(println) ident(e)operator(.)ident(realName)
+comment(// =>)
+comment(// Employee Number 12349876)
+comment(// Jason Smythe)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy's built in constructor and auto getter/setter features)
+ comment(// give you the required functionalty already but you could also)
+ comment(// override invokeMethod(\) for trickier scenarios.)
+type(class) class(Person8) operator({)
+ keyword(def) ident(name)operator(,) ident(age)operator(,) ident(peers)operator(,) ident(parent)
+ keyword(def) method(newChild)operator(()ident(args)(\)) operator({) keyword(new) ident(Person8)operator(()key(parent)operator(:)local_variable(this)operator(,) operator(*)operator(:)ident(args)(\)) (})
+(})
+
+ident(dad) operator(=) keyword(new) ident(Person8)operator(()key(name)operator(:)string<delimiter(')content(Jason)delimiter(')>operator(,) key(age)operator(:)integer(23)(\))
+ident(kid) operator(=) ident(dad)operator(.)ident(newChild)operator(()key(name)operator(:)string<delimiter(')content(Rachel)delimiter(')>operator(,) key(age)operator(:)integer(2)(\))
+ident(println) string<delimiter(")content(Kid's parent is )inline<inline_delimiter(${)ident(kid.parent.name)inline_delimiter(})>delimiter(")>
+comment(// => Kid's parent is Jason)
+
+comment(// additional fields ...)
+type(class) class(Employee3) directive(extends) ident(Person8) operator({) keyword(def) ident(salary)operator(,) ident(boss) (})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.12)
+comment(//----------------------------------------------------------------------------------)
+comment(// Fields marked as private in Groovy can't be trampled by another class in)
+comment(// the class hierarchy)
+type(class) class(Parent) operator({)
+ directive(private) ident(name) comment(// my child's name)
+ keyword(def) method(setChildName)operator(()ident(value)(\)) operator({) ident(name) operator(=) ident(value) (})
+ keyword(def) method(getChildName)operator(()(\)) operator({) ident(name) (})
+(})
+type(class) class(GrandParent) directive(extends) ident(Parent) operator({)
+ directive(private) ident(name) comment(// my grandchild's name)
+ keyword(def) method(setgrandChildName)operator(()ident(value)(\)) operator({) ident(name) operator(=) ident(value) (})
+ keyword(def) method(getGrandChildName)operator(()(\)) operator({) ident(name) (})
+(})
+ident(g) operator(=) keyword(new) ident(GrandParent)operator(()(\))
+ident(g)operator(.)ident(childName) operator(=) string<delimiter(')content(Jason)delimiter(')>
+ident(g)operator(.)ident(grandChildName) operator(=) string<delimiter(')content(Rachel)delimiter(')>
+ident(println) ident(g)operator(.)ident(childName) comment(// => Jason)
+ident(println) ident(g)operator(.)ident(grandChildName) comment(// => Rachel)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// The JVM garbage collector copes with circular structures.)
+comment(// You can test it with this code:)
+type(class) class(Person9) operator({)
+ keyword(def) ident(friend)
+ type(void) ident(finalize)operator(()(\)) operator({)
+ ident(println) string<delimiter(")content(Object [internal id=)inline<inline_delimiter(${)ident(hashCode(\))inline_delimiter(})>content(] is dying at )inline<inline_delimiter(${)ident(new Date(\))inline_delimiter(})>delimiter(")>
+ (})
+(})
+
+keyword(def) method(makeSomeFriends)operator(()(\)) operator({)
+ keyword(def) ident(first) operator(=) keyword(new) ident(Person9)operator(()(\))
+ keyword(def) ident(second) operator(=) keyword(new) ident(Person9)operator(()key(friend)operator(:)ident(first)(\))
+ keyword(def) ident(third) operator(=) keyword(new) ident(Person9)operator(()key(friend)operator(:)ident(second)(\))
+ keyword(def) ident(fourth) operator(=) keyword(new) ident(Person9)operator(()key(friend)operator(:)ident(third)(\))
+ keyword(def) ident(fifth) operator(=) keyword(new) ident(Person9)operator(()key(friend)operator(:)ident(fourth)(\))
+ ident(first)operator(.)ident(friend) operator(=) ident(fifth)
+(})
+
+ident(makeSomeFriends)operator(()(\))
+integer(100)operator(.)ident(times)operator({)
+ pre_type(System)operator(.)ident(gc)operator(()(\))
+(})
+comment(// =>)
+comment(// Object [internal id=24478976] is dying at Tue Jan 09 22:24:31 EST 2007)
+comment(// Object [internal id=32853087] is dying at Tue Jan 09 22:24:31 EST 2007)
+comment(// Object [internal id=23664622] is dying at Tue Jan 09 22:24:31 EST 2007)
+comment(// Object [internal id=10630672] is dying at Tue Jan 09 22:24:31 EST 2007)
+comment(// Object [internal id=25921812] is dying at Tue Jan 09 22:24:31 EST 2007)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.14)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy provides numerous methods which are automatically associated with)
+comment(// symbol operators, e.g. here is '<=>' which is associated with compareTo(\))
+comment(// Suppose we have a class with a compareTo operator, such as:)
+type(class) class(Person10) directive(implements) pre_type(Comparable) operator({)
+ keyword(def) ident(firstname)operator(,) ident(initial)operator(,) ident(surname)
+ ident(Person10)operator(()ident(f)operator(,)ident(i)operator(,)ident(s)(\)) operator({) ident(firstname) operator(=) ident(f)operator(;) ident(initial) operator(=) ident(i)operator(;) ident(surname) operator(=) ident(s) (})
+ type(int) ident(compareTo)operator(()ident(other)(\)) operator({) ident(firstname) operator(<=)operator(>) ident(other)operator(.)ident(firstname) (})
+(})
+ident(a) operator(=) keyword(new) ident(Person10)operator(()string<delimiter(')content(James)delimiter(')>operator(,) string<delimiter(')content(T)delimiter(')>operator(,) string<delimiter(')content(Kirk)delimiter(')>(\))
+ident(b) operator(=) keyword(new) ident(Person10)operator(()string<delimiter(')content(Samuel)delimiter(')>operator(,) string<delimiter(')content(L)delimiter(')>operator(,) string<delimiter(')content(Jackson)delimiter(')>(\))
+ident(println) ident(a) operator(<=)operator(>) ident(b)
+comment(// => -1)
+
+comment(// we can override the existing Person10's <=> operator as below)
+comment(// so that now comparisons are made using the middle initial)
+comment(// instead of the fisrtname:)
+type(class) class(Person11) directive(extends) ident(Person10) operator({)
+ ident(Person11)operator(()ident(f)operator(,)ident(i)operator(,)ident(s)(\)) operator({) local_variable(super)operator(()ident(f)operator(,)ident(i)operator(,)ident(s)(\)) (})
+ type(int) ident(compareTo)operator(()ident(other)(\)) operator({) ident(initial) operator(<=)operator(>) ident(other)operator(.)ident(initial) (})
+(})
+
+ident(a) operator(=) keyword(new) ident(Person11)operator(()string<delimiter(')content(James)delimiter(')>operator(,) string<delimiter(')content(T)delimiter(')>operator(,) string<delimiter(')content(Kirk)delimiter(')>(\))
+ident(b) operator(=) keyword(new) ident(Person11)operator(()string<delimiter(')content(Samuel)delimiter(')>operator(,) string<delimiter(')content(L)delimiter(')>operator(,) string<delimiter(')content(Jackson)delimiter(')>(\))
+ident(println) ident(a) operator(<=)operator(>) ident(b)
+comment(// => 1)
+
+comment(// we could also in general use Groovy's categories to extend class functionality.)
+
+comment(// There is no way to directly overload the '""' (stringify\))
+comment(// operator in Groovy. However, by convention, classes which)
+comment(// can reasonably be converted to a String will define a)
+comment(// 'toString(\)' method as in the TimeNumber class defined below.)
+comment(// The 'println' method will automatcally call an object's)
+comment(// 'toString(\)' method as is demonstrated below. Furthermore,)
+comment(// an object of that class can be used most any place where the)
+comment(// interpreter is looking for a String value.)
+
+comment(//---------------------------------------)
+comment(// NOTE: Groovy has various built-in Time/Date/Calendar classes)
+comment(// which would usually be used to manipulate time objects, the)
+comment(// following is supplied for educational purposes to demonstrate)
+comment(// operator overloading.)
+type(class) class(TimeNumber) operator({)
+ keyword(def) ident(h)operator(,) ident(m)operator(,) ident(s)
+ ident(TimeNumber)operator(()ident(hour)operator(,) ident(min)operator(,) ident(sec)(\)) operator({) ident(h) operator(=) ident(hour)operator(;) ident(m) operator(=) ident(min)operator(;) ident(s) operator(=) ident(sec) (})
+
+ keyword(def) method(toDigits)operator(()ident(s)(\)) operator({) ident(s)operator(.)ident(toString)operator(()(\))operator(.)ident(padLeft)operator(()integer(2)operator(,) string<delimiter(')content(0)delimiter(')>(\)) (})
+ pre_type(String) ident(toString)operator(()(\)) operator({)
+ keyword(return) ident(toDigits)operator(()ident(h)(\)) operator(+) string<delimiter(')content(:)delimiter(')> operator(+) ident(toDigits)operator(()ident(m)(\)) operator(+) string<delimiter(')content(:)delimiter(')> operator(+) ident(toDigits)operator(()ident(s)(\))
+ (})
+
+ keyword(def) method(plus)operator(()ident(other)(\)) operator({)
+ ident(s) operator(=) ident(s) operator(+) ident(other)operator(.)ident(s)
+ ident(m) operator(=) ident(m) operator(+) ident(other)operator(.)ident(m)
+ ident(h) operator(=) ident(h) operator(+) ident(other)operator(.)ident(h)
+ keyword(if) operator(()ident(s) operator(>=) integer(60)(\)) operator({)
+ ident(s) operator(%=) integer(60)
+ ident(m) operator(+=) integer(1)
+ (})
+ keyword(if) operator(()ident(m) operator(>=) integer(60)(\)) operator({)
+ ident(m) operator(%=) integer(60)
+ ident(h) operator(+=) integer(1)
+ (})
+ keyword(return) keyword(new) ident(TimeNumber)operator(()ident(h)operator(,) ident(m)operator(,) ident(s)(\))
+ (})
+
+(})
+
+ident(t1) operator(=) keyword(new) ident(TimeNumber)operator(()integer(0)operator(,) integer(58)operator(,) integer(59)(\))
+ident(sec) operator(=) keyword(new) ident(TimeNumber)operator(()integer(0)operator(,) integer(0)operator(,) integer(1)(\))
+ident(min) operator(=) keyword(new) ident(TimeNumber)operator(()integer(0)operator(,) integer(1)operator(,) integer(0)(\))
+ident(println) ident(t1) operator(+) ident(sec) operator(+) ident(min) operator(+) ident(min)
+
+comment(//-----------------------------)
+comment(// StrNum class example: Groovy's builtin String class already has the)
+comment(// capabilities outlined in StrNum Perl example, however the '*' operator)
+comment(// on Groovy's String class acts differently: It creates a string which)
+comment(// is the original string repeated N times.)
+comment(//)
+comment(// Using Groovy's String class as is in this example:)
+ident(x) operator(=) string<delimiter(")content(Red)delimiter(")>operator(;) ident(y) operator(=) string<delimiter(")content(Black)delimiter(")>
+ident(z) operator(=) ident(x)operator(+)ident(y)
+ident(r) operator(=) ident(z)operator(*)integer(3) comment(// r is "RedBlackRedBlackRedBlack")
+ident(println) string<delimiter(")content(values are )inline<inline_delimiter($)ident(x)>content(, )inline<inline_delimiter($)ident(y)>content(, )inline<inline_delimiter($)ident(z)>content(, and )inline<inline_delimiter($)ident(r)>delimiter(")>
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(x)>content( is )inline<inline_delimiter(${)ident(x < y ? 'LT' : 'GE')inline_delimiter(})>content( )inline<inline_delimiter($)ident(y)>delimiter(")>
+comment(// prints:)
+comment(// values are Red, Black, RedBlack, and RedBlackRedBlackRedBlack)
+comment(// Red is GE Black)
+
+comment(//-----------------------------)
+type(class) class(FixNum) operator({)
+ keyword(def) ident(REGEX) operator(=) regexp<delimiter(/)content(()content(\\.)char(\\d)content(*\))delimiter(/)>
+ directive(static) directive(final) ident(DEFAULT_PLACES) operator(=) integer(0)
+ keyword(def) type(float) ident(value)
+ keyword(def) type(int) ident(places)
+ ident(FixNum)operator(()ident(value)(\)) operator({)
+ ident(initValue)operator(()ident(value)(\))
+ keyword(def) ident(m) operator(=) ident(value)operator(.)ident(toString)operator(()(\)) operator(=~) ident(REGEX)
+ keyword(if) operator(()ident(m)(\)) ident(places) operator(=) ident(m)operator([)integer(0)(])operator([)integer(1)(])operator(.)ident(size)operator(()(\)) operator(-) integer(1)
+ keyword(else) ident(places) operator(=) ident(DEFAULT_PLACES)
+ (})
+ ident(FixNum)operator(()ident(value)operator(,) ident(places)(\)) operator({)
+ ident(initValue)operator(()ident(value)(\))
+ local_variable(this)operator(.)ident(places) operator(=) ident(places)
+ (})
+ directive(private) ident(initValue)operator(()ident(value)(\)) operator({)
+ local_variable(this)operator(.)ident(value) operator(=) ident(value)
+ (})
+
+ keyword(def) method(plus)operator(()ident(other)(\)) operator({)
+ keyword(new) ident(FixNum)operator(()ident(value) operator(+) ident(other)operator(.)ident(value)operator(,) operator([)ident(places)operator(,) ident(other)operator(.)ident(places)(])operator(.)ident(max)operator(()(\)\))
+ (})
+
+ keyword(def) method(multiply)operator(()ident(other)(\)) operator({)
+ keyword(new) ident(FixNum)operator(()ident(value) operator(*) ident(other)operator(.)ident(value)operator(,) operator([)ident(places)operator(,) ident(other)operator(.)ident(places)(])operator(.)ident(max)operator(()(\)\))
+ (})
+
+ keyword(def) method(div)operator(()ident(other)(\)) operator({)
+ ident(println) string<delimiter(")content(DEUG: Divide = )inline<inline_delimiter(${)ident(value/other.value)inline_delimiter(})>delimiter(")>
+ keyword(def) ident(result) operator(=) keyword(new) ident(FixNum)operator(()ident(value)operator(/)ident(other)operator(.)ident(value)(\))
+ ident(result)operator(.)ident(places) operator(=) operator([)ident(places)operator(,)ident(other)operator(.)ident(places)(])operator(.)ident(max)operator(()(\))
+ ident(result)
+ (})
+
+ pre_type(String) ident(toString)operator(()(\)) operator({)
+ comment(//m = value.toString(\) =~ /(\\d\)/ + REGEX)
+ pre_type(String)operator(.)ident(format)operator(()string<delimiter(")content(STR%s: %.)inline<inline_delimiter(${)ident(places)inline_delimiter(})>content(f)delimiter(")>operator(,) operator([)local_variable(this)operator(.)ident(class)operator(.)ident(name)operator(,) ident(value) keyword(as) type(float)(]) keyword(as) pre_type(Object)type([])(\))
+ (})
+(})
+
+ident(x) operator(=) keyword(new) ident(FixNum)operator(()integer(40)(\))
+ident(y) operator(=) keyword(new) ident(FixNum)operator(()integer(12)operator(,) integer(0)(\))
+
+ident(println) string<delimiter(")content(sum of )inline<inline_delimiter($)ident(x)>content( and )inline<inline_delimiter($)ident(y)>content( is )inline<inline_delimiter(${)ident(x+y)inline_delimiter(})>delimiter(")>
+ident(println) string<delimiter(")content(product of )inline<inline_delimiter($)ident(x)>content( and )inline<inline_delimiter($)ident(y)>content( is )inline<inline_delimiter(${)ident(x*y)inline_delimiter(})>delimiter(")>
+
+ident(z) operator(=) ident(x)operator(/)ident(y)
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(z)>content( has )inline<inline_delimiter($)ident(z)>content(.places places)delimiter(")>
+ident(z)operator(.)ident(places) operator(=) integer(2)
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(z)>content( now has )inline<inline_delimiter($)ident(z)>content(.places places)delimiter(")>
+
+ident(println) string<delimiter(")content(div of )inline<inline_delimiter($)ident(x)>content( by )inline<inline_delimiter($)ident(y)>content( is )inline<inline_delimiter($)ident(z)>delimiter(")>
+ident(println) string<delimiter(")content(square of that is )inline<inline_delimiter(${)ident(z*z)inline_delimiter(})>delimiter(")>
+comment(// =>)
+comment(// sum of STRFixNum: 40 and STRFixNum: 12 is STRFixNum: 52)
+comment(// product of STRFixNum: 40 and STRFixNum: 12 is STRFixNum: 480)
+comment(// DEUG: Divide = 3.3333333333333335)
+comment(// STRFixNum: 3 has 0 places)
+comment(// STRFixNum: 3.33 now has 2 places)
+comment(// div of STRFixNum: 40 by STRFixNum: 12 is STRFixNum: 3.33)
+comment(// square of that is STRFixNum: 11.11)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_13.15)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy doesn't use the tie terminology but you can achieve)
+comment(// similar results with Groovy's metaprogramming facilities)
+type(class) class(ValueRing) operator({)
+ directive(private) ident(values)
+ keyword(def) method(add)operator(()ident(value)(\)) operator({) ident(values)operator(.)ident(add)operator(()integer(0)operator(,) ident(value)(\)) (})
+ keyword(def) method(next)operator(()(\)) operator({)
+ keyword(def) ident(head) operator(=) ident(values)operator([)integer(0)(])
+ ident(values) operator(=) ident(values)operator([)integer(1)operator(..)operator(-)integer(1)(]) operator(+) ident(head)
+ keyword(return) ident(head)
+ (})
+(})
+ident(ring) operator(=) keyword(new) ident(ValueRing)operator(()key(values)operator(:)operator([)string<delimiter(')content(red)delimiter(')>operator(,) string<delimiter(')content(blue)delimiter(')>(]\))
+keyword(def) method(getColor)operator(()(\)) operator({) ident(ring)operator(.)ident(next)operator(()(\)) (})
+type(void) ident(setProperty)operator(()pre_type(String) ident(n)operator(,) ident(v)(\)) operator({)
+ keyword(if) operator(()ident(n) operator(==) string<delimiter(')content(color)delimiter(')>(\)) operator({) ident(ring)operator(.)ident(add)operator(()ident(v)(\))operator(;) keyword(return) (})
+ local_variable(super)operator(.)ident(setProperty)operator(()ident(n)operator(,)ident(v)(\))
+(})
+
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(color)>content( )inline<inline_delimiter($)ident(color)>content( )inline<inline_delimiter($)ident(color)>content( )inline<inline_delimiter($)ident(color)>content( )inline<inline_delimiter($)ident(color)>content( )inline<inline_delimiter($)ident(color)>delimiter(")>
+comment(// => red blue red blue red blue)
+
+ident(color) operator(=) string<delimiter(')content(green)delimiter(')>
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(color)>content( )inline<inline_delimiter($)ident(color)>content( )inline<inline_delimiter($)ident(color)>content( )inline<inline_delimiter($)ident(color)>content( )inline<inline_delimiter($)ident(color)>content( )inline<inline_delimiter($)ident(color)>delimiter(")>
+comment(// => green red blue green red blue)
+
+comment(// Groovy doesn't have the $_ implicit variable so we can't show an)
+comment(// example that gets rid of it. We can however show an example of how)
+comment(// you could add in a simplified version of that facility into Groovy.)
+comment(// We use Groovy's metaProgramming facilities. We execute our script)
+comment(// in a new GroovyShell so that we don't affect subsequent examples.)
+comment(// script:)
+ident(x) operator(=) integer(3)
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(_)>delimiter(")>
+ident(y) operator(=) string<delimiter(')content(cat)delimiter(')> operator(*) ident(x)
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(_)>delimiter(")>
+
+comment(// metaUnderscore:)
+type(void) ident(setProperty)operator(()pre_type(String) ident(n)operator(,) ident(v)(\)) operator({)
+ local_variable(super)operator(.)ident(setProperty)operator(()string<delimiter(')content(_)delimiter(')>operator(,)ident(v)(\))
+ local_variable(super)operator(.)ident(setProperty)operator(()ident(n)operator(,)ident(v)(\))
+(})
+
+keyword(new) ident(GroovyShell)operator(()(\))operator(.)ident(evaluate)operator(()ident(metaUnderscore) operator(+) ident(script)(\))
+comment(// =>)
+comment(// 3)
+comment(// catcatcat)
+
+comment(// We can get a little bit fancier by making an UnderscoreAware class)
+comment(// that wraps up some of this functionality. This is not recommended)
+comment(// as good Groovy style but mimicks the $_ behaviour in a sinple way.)
+type(class) class(UnderscoreAware) directive(implements) ident(GroovyInterceptable) operator({)
+ directive(private) ident(_saved)
+ type(void) ident(setProperty)operator(()pre_type(String) ident(n)operator(,) ident(v)(\)) operator({)
+ ident(_saved) operator(=) ident(v)
+ local_variable(this)operator(.)ident(metaClass)operator(.)ident(setProperty)operator(()local_variable(this)operator(,) ident(n)operator(,) ident(v)(\))
+ (})
+ keyword(def) method(getProperty)operator(()pre_type(String) ident(n)(\)) operator({)
+ keyword(if) operator(()ident(n) operator(==) string<delimiter(')content(_)delimiter(')>(\)) keyword(return) ident(_saved)
+ local_variable(this)operator(.)ident(metaClass)operator(.)ident(getProperty)operator(()local_variable(this)operator(,) ident(n)(\))
+ (})
+ keyword(def) method(invokeMethod)operator(()pre_type(String) ident(name)operator(,) pre_type(Object) ident(args)(\)) operator({)
+ keyword(if) operator(()ident(name)operator(.)ident(startsWith)operator(()string<delimiter(')content(print)delimiter(')>(\)) operator(&&) ident(args)operator(.)ident(size)operator(()(\)) operator(==) integer(0)(\))
+ ident(args) operator(=) operator([)ident(_saved)(]) keyword(as) pre_type(Object)type([])
+ local_variable(this)operator(.)ident(metaClass)operator(.)ident(invokeMethod)operator(()local_variable(this)operator(,) ident(name)operator(,) ident(args)(\))
+ (})
+(})
+
+type(class) class(PerlishClass) directive(extends) ident(UnderscoreAware) operator({)
+ directive(private) ident(_age)
+ keyword(def) method(setAge)operator(()ident(age)(\))operator({) ident(_age) operator(=) ident(age) (})
+ keyword(def) method(getAge)operator(()(\))operator({) ident(_age) (})
+ keyword(def) method(test)operator(()(\)) operator({)
+ ident(age) operator(=) integer(25)
+ ident(println) string<delimiter(")inline<inline_delimiter($)ident(_)>delimiter(")> comment(// explicit $_ supported)
+ ident(age)operator(++)
+ ident(println)operator(()(\)) comment(// implicit $_ will be injected)
+ (})
+(})
+
+keyword(def) ident(x) operator(=) keyword(new) ident(PerlishClass)operator(()(\))
+ident(x)operator(.)ident(test)operator(()(\))
+comment(// =>)
+comment(// 25)
+comment(// 26)
+
+comment(// Autoappending hash:)
+type(class) class(AutoMap) directive(extends) pre_type(HashMap) operator({)
+ type(void) ident(setProperty)operator(()pre_type(String) ident(name)operator(,) ident(v)(\)) operator({)
+ keyword(if) operator(()ident(containsKey)operator(()ident(name)(\)\)) operator({)
+ ident(put)operator(()ident(name)operator(,) ident(get)operator(()ident(name)(\)) operator(+) ident(v)(\))
+ (}) keyword(else) operator({)
+ ident(put)operator(()ident(name)operator(,) operator([)ident(v)(]\))
+ (})
+ (})
+(})
+ident(m) operator(=) keyword(new) ident(AutoMap)operator(()(\))
+ident(m)operator(.)ident(beer) operator(=) string<delimiter(')content(guinness)delimiter(')>
+ident(m)operator(.)ident(food) operator(=) string<delimiter(')content(potatoes)delimiter(')>
+ident(m)operator(.)ident(food) operator(=) string<delimiter(')content(peas)delimiter(')>
+ident(println) ident(m)
+comment(// => ["food":["potatoes", "peas"], "beer":["guinness"]])
+
+comment(// Case-Insensitive Hash:)
+type(class) class(FoldedMap) directive(extends) pre_type(HashMap) operator({)
+ type(void) ident(setProperty)operator(()pre_type(String) ident(name)operator(,) ident(v)(\)) operator({)
+ ident(put)operator(()ident(name)operator(.)ident(toLowerCase)operator(()(\))operator(,) ident(v)(\))
+ (})
+ keyword(def) method(getProperty)operator(()pre_type(String) ident(name)(\)) operator({)
+ ident(get)operator(()ident(name)operator(.)ident(toLowerCase)operator(()(\)\))
+ (})
+(})
+ident(tab) operator(=) keyword(new) ident(FoldedMap)operator(()(\))
+ident(tab)operator(.)ident(VILLAIN) operator(=) string<delimiter(')content(big )delimiter(')>
+ident(tab)operator(.)ident(herOine) operator(=) string<delimiter(')content(red riding hood)delimiter(')>
+ident(tab)operator(.)ident(villain) operator(+=) string<delimiter(')content(bad wolf)delimiter(')>
+ident(println) ident(tab)
+comment(// => ["heroine":"red riding hood", "villain":"big bad wolf"])
+
+comment(// Hash That "Allows Look-Ups by Key or Value":)
+type(class) class(RevMap) directive(extends) pre_type(HashMap) operator({)
+ type(void) ident(setProperty)operator(()pre_type(String) ident(n)operator(,) ident(v)(\)) operator({) ident(put)operator(()ident(n)operator(,)ident(v)(\))operator(;) ident(put)operator(()ident(v)operator(,)ident(n)(\)) (})
+ type(void) ident(remove)operator(()ident(n)(\)) operator({) local_variable(super)operator(.)ident(remove)operator(()ident(get)operator(()ident(n)(\)\))operator(;) local_variable(super)operator(.)ident(remove)operator(()ident(n)(\)) (})
+(})
+ident(rev) operator(=) keyword(new) ident(RevMap)operator(()(\))
+ident(rev)operator(.)ident(Rojo) operator(=) string<delimiter(')content(Red)delimiter(')>
+ident(rev)operator(.)ident(Azul) operator(=) string<delimiter(')content(Blue)delimiter(')>
+ident(rev)operator(.)ident(Verde) operator(=) string<delimiter(')content(Green)delimiter(')>
+ident(rev)operator(.)ident(EVIL) operator(=) operator([) string<delimiter(")content(No way!)delimiter(")>operator(,) string<delimiter(")content(Way!!)delimiter(")> (])
+ident(rev)operator(.)ident(remove)operator(()string<delimiter(')content(Red)delimiter(')>(\))
+ident(rev)operator(.)ident(remove)operator(()string<delimiter(')content(Azul)delimiter(')>(\))
+ident(println) ident(rev)
+comment(// =>)
+comment(// [["No way!", "Way!!"]:"EVIL", "EVIL":["No way!", "Way!!"], "Verde":"Green", "Green":"Verde"])
+
+comment(// Infinite loop scenario:)
+comment(// def x(n\) { x(++n\) }; x(0\))
+comment(// => Caught: java.lang.StackOverflowError)
+
+comment(// Multiple Strrams scenario:)
+type(class) class(MultiStream) directive(extends) pre_type(PrintStream) operator({)
+ keyword(def) ident(streams)
+ ident(MultiStream)operator(()pre_type(List) ident(streams)(\)) operator({)
+ local_variable(super)operator(()ident(streams)operator([)integer(0)(]\))
+ local_variable(this)operator(.)ident(streams) operator(=) ident(streams)
+ (})
+ keyword(def) method(println)operator(()pre_type(String) ident(x)(\)) operator({)
+ ident(streams)operator(.)ident(each)operator({) local_variable(it)operator(.)ident(println)operator(()ident(x)(\)) (})
+ (})
+(})
+ident(tee) operator(=) keyword(new) ident(MultiStream)operator(()operator([)pre_type(System)operator(.)ident(out)operator(,) pre_type(System)operator(.)ident(err)(]\))
+ident(tee)operator(.)ident(println) operator(()string<delimiter(')content(This goes two places)delimiter(')>(\))
+comment(// =>)
+comment(// This goes two places)
+comment(// This goes two places)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_14.0)
+comment(//----------------------------------------------------------------------------------)
+ident(As) ident(discussed) keyword(in) float(14.1)operator(,) ident(many) ident(database) ident(options) ident(exist)operator(,) ident(one) ident(of) ident(which) ident(is) ident(JDBC)operator(.)
+ident(Over) integer(200) ident(JDBC) ident(drivers) ident(are) ident(listed) ident(at) ident(the) ident(following) pre_type(URL)operator(:)
+key(http)operator(:)comment(//developers.sun.com/product/jdbc/drivers/browse_all.jsp)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_14.1)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy can make use of various Java persistence libraries and has special)
+comment(// support built-in (e.g. datasets\) for interacting wth RDBMS systems.)
+comment(// Some of the options include:)
+comment(// object serialization (built in to Java\))
+comment(// pbeans: pbeans.sf.net)
+comment(// prevayler: http://www.prevayler.org)
+comment(// Berkeley DB Java edition: http://www.oracle.com/database/berkeley-db/je/)
+comment(// JDBC: Over 200 drivers are listed at http://developers.sun.com/product/jdbc/drivers)
+comment(// Datasets (special Groovy support\))
+comment(// XML via e.g. xstream or JAXB or XmlBeans or ...)
+comment(// ORM: over 20 are listed at http://java-source.net/open-source/persistence)
+comment(// JNI: can be used directly on a platform that supports e.g. DBM or via)
+comment(// a cross platform API such as Apache APR which includes DBM routines:)
+comment(// http://apr.apache.org/docs/apr-util/0.9/group__APR__Util__DBM.html)
+comment(// jmork: used for Firefox/Thunderbird databases, e.g. address books, history files)
+comment(// JDBC or Datasets would normally be most common for all examples in this chapter.)
+
+
+comment(// Example shown using berkeley db Java edition - not quite as transparent as)
+comment(// cookbook example as Berkeley DB Java addition makes transactions visible.)
+keyword(import) include(com.sleepycat.je.*)
+ident(tx) operator(=) keyword(null)
+ident(envHome) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(")content(D:/Projects/GroovyExamples/Pleac/data/db)delimiter(")>(\))
+
+ident(myEnvConfig) operator(=) keyword(new) ident(EnvironmentConfig)operator(()(\))
+ident(myEnvConfig)operator(.)ident(setAllowCreate)operator(()keyword(true)(\))
+ident(myEnv) operator(=) keyword(new) ident(Environment)operator(()ident(envHome)operator(,) ident(myEnvConfig)(\))
+
+ident(myDbConfig) operator(=) keyword(new) ident(DatabaseConfig)operator(()(\))
+ident(myDbConfig)operator(.)ident(setAllowCreate)operator(()keyword(true)(\))
+ident(myDb) operator(=) ident(myEnv)operator(.)ident(openDatabase)operator(()ident(tx)operator(,) string<delimiter(")content(vendorDB)delimiter(")>operator(,) ident(myDbConfig)(\))
+
+ident(theKey) operator(=) keyword(new) ident(DatabaseEntry)operator(()string<delimiter(")content(key)delimiter(")>operator(.)ident(getBytes)operator(()string<delimiter(")content(UTF-8)delimiter(")>(\)\))
+ident(theData) operator(=) keyword(new) ident(DatabaseEntry)operator(()string<delimiter(")content(data)delimiter(")>operator(.)ident(getBytes)operator(()string<delimiter(")content(UTF-8)delimiter(")>(\)\))
+ident(myDb)operator(.)ident(put)operator(()ident(tx)operator(,) ident(theKey)operator(,) ident(theData)(\))
+keyword(if) operator(()ident(myDb)operator(.)ident(get)operator(()ident(tx)operator(,) ident(theKey)operator(,) ident(theData)operator(,) ident(LockMode)operator(.)ident(DEFAULT)(\)) operator(==) ident(OperationStatus)operator(.)ident(SUCCESS)(\)) operator({)
+ ident(key) operator(=) keyword(new) pre_type(String)operator(()ident(theKey)operator(.)ident(data)operator(,) string<delimiter(")content(UTF-8)delimiter(")>(\))
+ ident(foundData) operator(=) keyword(new) pre_type(String)operator(()ident(theData)operator(.)ident(data)operator(,) string<delimiter(")content(UTF-8)delimiter(")>(\))
+ ident(println) string<delimiter(")content(For key: ')inline<inline_delimiter($)ident(key)>content(' found data: ')inline<inline_delimiter($)ident(foundData)>content('.)delimiter(")>
+(})
+ident(myDb)operator(.)ident(delete)operator(()ident(tx)operator(,) ident(theKey)(\))
+ident(myDb)operator(.)ident(close)operator(()(\))
+ident(myEnv)operator(.)ident(close)operator(()(\))
+
+
+comment(// userstats using pbeans)
+keyword(import) include(net.sourceforge.pbeans.*)
+comment(// on *nix use: whotext = "who".execute(\).text)
+ident(whotext) operator(=) string<delimiter(''')content(
+gnat ttyp1 May 29 15:39 (coprolith.frii.com\)
+bill ttyp1 May 28 15:38 (hilary.com\)
+gnit ttyp1 May 27 15:37 (somewhere.org\)
+)delimiter(''')>
+
+type(class) class(LoginInfo) directive(implements) ident(Persistent) operator({)
+ ident(LoginInfo)operator(()(\)) operator({)(})
+ ident(LoginInfo)operator(()ident(name)(\)) operator({) local_variable(this)operator(.)ident(name) operator(=) ident(name)operator(;) ident(loginCount) operator(=) integer(1) (})
+ pre_type(String) ident(name)
+ type(int) ident(loginCount)
+(})
+
+keyword(def) method(printAllUsers)operator(()ident(store)(\)) operator({)
+ ident(printUsers)operator(()ident(store)operator(,) ident(store)operator(.)ident(select)operator(()ident(LoginInfo)operator(.)ident(class)(\))operator(.)ident(collect)operator({)local_variable(it)operator(.)ident(name)(})operator(.)ident(sort)operator(()(\)\))
+(})
+
+keyword(def) method(printUsers)operator(()ident(store)operator(,) ident(list)(\)) operator({)
+ ident(list)operator(.)ident(each)operator({)
+ ident(println) string<delimiter(")inline<inline_delimiter($)local_variable(it)>content( )inline<inline_delimiter(${)ident(store.selectSingle(LoginInfo.class, 'name', it\).loginCount)inline_delimiter(})>delimiter(")>
+ (})
+(})
+
+keyword(def) method(addUsers)operator(()ident(store)(\)) operator({)
+ ident(whotext)operator(.)ident(trim)operator(()(\))operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(each)operator({)
+ ident(m) operator(=) local_variable(it) operator(=~) regexp<delimiter(/)content(^()char(\\S)content(+\))delimiter(/)>
+ ident(name) operator(=) ident(m)operator([)integer(0)(])operator([)integer(1)(])
+ ident(item) operator(=) ident(store)operator(.)ident(selectSingle)operator(()ident(LoginInfo)operator(.)ident(class)operator(,) string<delimiter(')content(name)delimiter(')>operator(,) ident(name)(\))
+ keyword(if) operator(()ident(item)(\)) operator({)
+ ident(item)operator(.)ident(loginCount)operator(++)
+ ident(store)operator(.)ident(save)operator(()ident(item)(\))
+ (}) keyword(else) operator({)
+ ident(store)operator(.)ident(insert)operator(()keyword(new) ident(LoginInfo)operator(()ident(name)(\)\))
+ (})
+ (})
+(})
+
+keyword(def) ident(ds) operator(=) keyword(new) ident(org)operator(.)ident(hsqldb)operator(.)ident(jdbc)operator(.)ident(jdbcDataSource)operator(()(\))
+ident(ds)operator(.)ident(database) operator(=) string<delimiter(')content(jdbc:hsqldb:hsql://localhost/mydb)delimiter(')>
+ident(ds)operator(.)ident(user) operator(=) string<delimiter(')content(sa)delimiter(')>
+ident(ds)operator(.)ident(password) operator(=) string<delimiter(')delimiter(')>
+ident(store) operator(=) keyword(new) ident(Store)operator(()ident(ds)(\))
+keyword(if) operator(()ident(args)operator(.)ident(size)operator(()(\)) operator(==) integer(0)(\)) operator({)
+ ident(addUsers)operator(()ident(store)(\))
+(}) keyword(else) keyword(if) operator(()ident(args) operator(==) operator([)string<delimiter(')content(ALL)delimiter(')>(]\)) operator({)
+ ident(printAllUsers)operator(()ident(store)(\))
+(}) keyword(else) operator({)
+ ident(printUsers)operator(()ident(store)operator(,) ident(args)(\))
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_14.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy would normally use JDBC here (see 14.1 for details\))
+keyword(import) include(com.sleepycat.je.*)
+ident(tx) operator(=) keyword(null)
+ident(envHome) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(")content(D:/Projects/GroovyExamples/Pleac/data/db)delimiter(")>(\))
+
+ident(myEnvConfig) operator(=) keyword(new) ident(EnvironmentConfig)operator(()(\))
+ident(myEnvConfig)operator(.)ident(setAllowCreate)operator(()keyword(true)(\))
+ident(myEnv) operator(=) keyword(new) ident(Environment)operator(()ident(envHome)operator(,) ident(myEnvConfig)(\))
+
+ident(myDbConfig) operator(=) keyword(new) ident(DatabaseConfig)operator(()(\))
+ident(myDbConfig)operator(.)ident(setAllowCreate)operator(()keyword(true)(\))
+ident(myDb) operator(=) ident(myEnv)operator(.)ident(openDatabase)operator(()ident(tx)operator(,) string<delimiter(")content(vendorDB)delimiter(")>operator(,) ident(myDbConfig)(\))
+
+ident(theKey) operator(=) keyword(new) ident(DatabaseEntry)operator(()string<delimiter(")content(key)delimiter(")>operator(.)ident(getBytes)operator(()string<delimiter(")content(UTF-8)delimiter(")>(\)\))
+ident(theData) operator(=) keyword(new) ident(DatabaseEntry)operator(()string<delimiter(")content(data)delimiter(")>operator(.)ident(getBytes)operator(()string<delimiter(")content(UTF-8)delimiter(")>(\)\))
+ident(myDb)operator(.)ident(put)operator(()ident(tx)operator(,) ident(theKey)operator(,) ident(theData)(\))
+ident(myDb)operator(.)ident(close)operator(()(\))
+comment(// clear out database)
+ident(returnCount) operator(=) keyword(true)
+ident(println) ident(myEnv)operator(.)ident(truncateDatabase)operator(()ident(tx)operator(,) string<delimiter(")content(vendorDB)delimiter(")>operator(,) ident(returnCount)(\)) operator(+) string<delimiter(')content( records deleted)delimiter(')>
+comment(// remove database)
+ident(myEnv)operator(.)ident(removeDatabase)operator(()ident(tx)operator(,) string<delimiter(")content(vendorDB)delimiter(")>(\))
+ident(myEnv)operator(.)ident(close)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_14.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// Original cookbook example not likely in Groovy.)
+comment(// Here is a more realistic example, copying pbeans -> jdbc)
+comment(// Creation of pbeans database not strictly needed but shown for completion)
+
+keyword(import) include(net.sourceforge.pbeans.*)
+keyword(import) include(groovy.sql.Sql)
+
+keyword(def) ident(ds) operator(=) keyword(new) ident(org)operator(.)ident(hsqldb)operator(.)ident(jdbc)operator(.)ident(jdbcDataSource)operator(()(\))
+ident(ds)operator(.)ident(database) operator(=) string<delimiter(')content(jdbc:hsqldb:hsql://localhost/mydb)delimiter(')>
+ident(ds)operator(.)ident(user) operator(=) string<delimiter(')content(sa)delimiter(')>
+ident(ds)operator(.)ident(password) operator(=) string<delimiter(')delimiter(')>
+ident(store) operator(=) keyword(new) ident(Store)operator(()ident(ds)(\))
+
+type(class) class(Person) directive(implements) ident(Persistent) operator({)
+ pre_type(String) ident(name)
+ pre_type(String) ident(does)
+ pre_type(String) ident(email)
+(})
+
+comment(// populate with test data)
+ident(store)operator(.)ident(insert)operator(()keyword(new) ident(Person)operator(()key(name)operator(:)string<delimiter(')content(Tom Christiansen)delimiter(')>operator(,) key(does)operator(:)string<delimiter(')content(book author)delimiter(')>operator(,) key(email)operator(:)string<delimiter(')content(tchrist@perl.com)delimiter(')>(\)\))
+ident(store)operator(.)ident(insert)operator(()keyword(new) ident(Person)operator(()key(name)operator(:)string<delimiter(')content(Tom Boutell)delimiter(')>operator(,) key(does)operator(:)string<delimiter(')content(Poet Programmer)delimiter(')>operator(,) key(email)operator(:)string<delimiter(')content(boutell@boutell.com)delimiter(')>(\)\))
+
+ident(people) operator(=) ident(store)operator(.)ident(select)operator(()ident(Person)operator(.)ident(class)(\))
+
+ident(db) operator(=) keyword(new) ident(Sql)operator(()ident(ds)(\))
+
+ident(db)operator(.)ident(execute) string<delimiter(')content(CREATE TABLE people ( name VARCHAR, does VARCHAR, email VARCHAR \);)delimiter(')>
+ident(people)operator(.)ident(each)operator({) ident(p) operator(->)
+ ident(db)operator(.)ident(execute) string<delimiter(")content(INSERT INTO people ( name, does, email \) VALUES ()inline<inline_delimiter($)ident(p)>content(.name,)inline<inline_delimiter($)ident(p)>content(.does,)inline<inline_delimiter($)ident(p)>content(.email\);)delimiter(")>
+(})
+ident(db)operator(.)ident(eachRow)operator(()string<delimiter(")content(SELECT * FROM people where does like 'book%')delimiter(")>(\))operator({)
+ ident(println) string<delimiter(")inline<inline_delimiter($)local_variable(it)>content(.name, )inline<inline_delimiter($)local_variable(it)>content(.does, )inline<inline_delimiter($)local_variable(it)>content(.email)delimiter(")>
+(})
+ident(db)operator(.)ident(execute) string<delimiter(')content(DROP TABLE people;)delimiter(')>
+comment(// => Tom Christiansen, book author, tchrist@perl.com)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_14.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy would normally use JDBC here (see 14.1 for details\))
+keyword(import) include(com.sleepycat.je.*)
+
+keyword(def) method(copyEntries)operator(()ident(indb)operator(,) ident(outdb)(\)) operator({)
+ ident(cursor) operator(=) ident(indb1)operator(.)ident(openCursor)operator(()keyword(null)operator(,) keyword(null)(\))
+ keyword(while) operator(()ident(cursor)operator(.)ident(getNext)operator(()ident(foundKey)operator(,) ident(foundData)operator(,) ident(LockMode)operator(.)ident(DEFAULT)(\)) operator(==) ident(OperationStatus)operator(.)ident(SUCCESS)(\))
+ ident(outdb)operator(.)ident(out)operator(()ident(tx)operator(,) ident(foundKey)operator(,) ident(foundData)(\))
+ ident(cursor)operator(.)ident(close)operator(()(\))
+(})
+
+ident(tx) operator(=) keyword(null)
+ident(envHome) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(")content(D:/Projects/GroovyExamples/Pleac/data/db)delimiter(")>(\))
+
+ident(myEnvConfig) operator(=) keyword(new) ident(EnvironmentConfig)operator(()(\))
+ident(myEnvConfig)operator(.)ident(setAllowCreate)operator(()keyword(true)(\))
+ident(myEnv) operator(=) keyword(new) ident(Environment)operator(()ident(envHome)operator(,) ident(myEnvConfig)(\))
+
+ident(myDbConfig) operator(=) keyword(new) ident(DatabaseConfig)operator(()(\))
+ident(myDbConfig)operator(.)ident(setAllowCreate)operator(()keyword(true)(\))
+ident(indb1) operator(=) ident(myEnv)operator(.)ident(openDatabase)operator(()ident(tx)operator(,) string<delimiter(")content(db1)delimiter(")>operator(,) ident(myDbConfig)(\))
+ident(indb2) operator(=) ident(myEnv)operator(.)ident(openDatabase)operator(()ident(tx)operator(,) string<delimiter(")content(db2)delimiter(")>operator(,) ident(myDbConfig)(\))
+ident(outdb) operator(=) ident(myEnv)operator(.)ident(openDatabase)operator(()ident(tx)operator(,) string<delimiter(")content(db3)delimiter(")>operator(,) ident(myDbConfig)(\))
+ident(foundKey) operator(=) keyword(new) ident(DatabaseEntry)operator(()(\))
+ident(foundData) operator(=) keyword(new) ident(DatabaseEntry)operator(()(\))
+ident(copyEntries)operator(()ident(indb1)operator(,) ident(outdb)(\))
+ident(copyEntries)operator(()ident(indb2)operator(,) ident(outdb)(\))
+ident(cursor) operator(=) ident(indb2)operator(.)ident(openCursor)operator(()keyword(null)operator(,) keyword(null)(\))
+keyword(while) operator(()ident(cursor)operator(.)ident(getNext)operator(()ident(foundKey)operator(,) ident(foundData)operator(,) ident(LockMode)operator(.)ident(DEFAULT)(\)) operator(==) ident(OperationStatus)operator(.)ident(SUCCESS)(\))
+ ident(outdb)operator(.)ident(out)operator(()ident(tx)operator(,) ident(foundKey)operator(,) ident(foundData)(\))
+ident(cursor)operator(.)ident(close)operator(()(\))
+ident(indb1)operator(.)ident(close)operator(()(\))
+ident(indb2)operator(.)ident(close)operator(()(\))
+ident(outdb)operator(.)ident(close)operator(()(\))
+ident(myEnv)operator(.)ident(close)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_14.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// If you are using a single file based persistence mechanism you can)
+comment(// use the file locking mechanisms mentioned in 7.11 otherwise the)
+comment(// database itself or the ORM layer will provide locking mechanisms.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_14.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// N/A for most Java/Groovy persistent technologies.)
+comment(// Use indexes for RDBMS systems.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_14.7)
+comment(//----------------------------------------------------------------------------------)
+ comment(// We can write a category that allows the ArrayList class)
+ comment(// to be persisted as required.)
+ type(class) class(ArrayListCategory) operator({)
+ directive(static) ident(file) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(/temp.txt)delimiter(')>(\))
+ directive(public) directive(static) type(void) ident(save)operator(()pre_type(ArrayList) ident(self)(\)) operator({)
+ keyword(def) ident(LS) operator(=) pre_type(System)operator(.)ident(getProperty)operator(()string<delimiter(')content(line.separator)delimiter(')>(\))
+ ident(file)operator(.)ident(withWriter)operator({) ident(w) operator(->)
+ ident(self)operator(.)ident(each)operator({) ident(w)operator(.)ident(write)operator(()local_variable(it) operator(+) ident(LS)(\)) (})
+ (})
+ (})
+ (})
+
+ ident(lines) operator(=) string<delimiter(''')content(
+ zero
+ one
+ two
+ three
+ four
+ )delimiter(''')>operator(.)ident(trim)operator(()(\))operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\)) keyword(as) pre_type(ArrayList)
+
+ ident(use)operator(()ident(ArrayListCategory)(\)) operator({)
+ ident(println) string<delimiter(")content(ORIGINAL)delimiter(")>
+ keyword(for) operator(()ident(i) keyword(in) integer(0)operator(..<)ident(lines)operator(.)ident(size)operator(()(\)\))
+ ident(println) string<delimiter(")inline<inline_delimiter(${)ident(i)inline_delimiter(})>content(: )inline<inline_delimiter(${)ident(lines[i])inline_delimiter(})>delimiter(")>
+
+ ident(a) operator(=) ident(lines)operator([)operator(-)integer(1)(])
+ ident(lines)operator([)operator(-)integer(1)(]) operator(=) string<delimiter(")content(last)delimiter(")>
+ ident(println) string<delimiter(")content(The last line was [)inline<inline_delimiter($)ident(a)>content(])delimiter(")>
+
+ ident(a) operator(=) ident(lines)operator([)integer(0)(])
+ ident(lines) operator(=) operator([)string<delimiter(")content(first)delimiter(")>(]) operator(+) ident(lines)operator([)integer(1)operator(..)operator(-)integer(1)(])
+ ident(println) string<delimiter(")content(The first line was [)inline<inline_delimiter($)ident(a)>content(])delimiter(")>
+
+ ident(lines)operator(.)ident(add)operator(()integer(3)operator(,) string<delimiter(')content(Newbie)delimiter(')>(\))
+ ident(lines)operator(.)ident(add)operator(()integer(1)operator(,) string<delimiter(')content(New One)delimiter(')>(\))
+
+ ident(lines)operator(.)ident(remove)operator(()integer(3)(\))
+
+ ident(println) string<delimiter(")content(REVERSE)delimiter(")>
+ operator(()ident(lines)operator(.)ident(size)operator(()(\)) operator(-) integer(1)(\))operator(.)ident(downto)operator(()integer(0)(\))operator({) ident(i) operator(->)
+ ident(println) string<delimiter(")inline<inline_delimiter(${)ident(i)inline_delimiter(})>content(: )inline<inline_delimiter(${)ident(lines[i])inline_delimiter(})>delimiter(")>
+ (})
+ ident(lines)operator(.)ident(save)operator(()(\))
+ (})
+ comment(// =>)
+ comment(// ORIGINAL)
+ comment(// 0: zero)
+ comment(// 1: one)
+ comment(// 2: two)
+ comment(// 3: three)
+ comment(// 4: four)
+ comment(// The last line was [four])
+ comment(// The first line was [zero])
+ comment(// REVERSE)
+ comment(// 5: last)
+ comment(// 4: three)
+ comment(// 3: Newbie)
+ comment(// 2: one)
+ comment(// 1: New One)
+ comment(// 0: first)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_14.8)
+comment(//----------------------------------------------------------------------------------)
+comment(// example using pbeans)
+keyword(import) include(net.sourceforge.pbeans.*)
+keyword(def) ident(ds) operator(=) keyword(new) ident(org)operator(.)ident(hsqldb)operator(.)ident(jdbc)operator(.)ident(jdbcDataSource)operator(()(\))
+ident(ds)operator(.)ident(database) operator(=) string<delimiter(')content(jdbc:hsqldb:hsql://localhost/mydb)delimiter(')>
+ident(ds)operator(.)ident(user) operator(=) string<delimiter(')content(sa)delimiter(')>
+ident(ds)operator(.)ident(password) operator(=) string<delimiter(')delimiter(')>
+ident(store) operator(=) keyword(new) ident(Store)operator(()ident(ds)(\))
+
+type(class) class(Person) directive(implements) ident(Persistent) operator({)
+ pre_type(String) ident(name)
+ pre_type(String) ident(does)
+ pre_type(String) ident(email)
+(})
+
+ident(name1) operator(=) string<delimiter(')content(Tom Christiansen)delimiter(')>
+ident(name2) operator(=) string<delimiter(')content(Tom Boutell)delimiter(')>
+
+ident(store)operator(.)ident(insert)operator(()keyword(new) ident(Person)operator(()key(name)operator(:)ident(name1)operator(,) key(does)operator(:)string<delimiter(')content(book author)delimiter(')>operator(,) key(email)operator(:)string<delimiter(')content(tchrist@perl.com)delimiter(')>(\)\))
+ident(store)operator(.)ident(insert)operator(()keyword(new) ident(Person)operator(()key(name)operator(:)ident(name2)operator(,) key(does)operator(:)string<delimiter(')content(shareware author)delimiter(')>operator(,) key(email)operator(:)string<delimiter(')content(boutell@boutell.com)delimiter(')>(\)\))
+
+ident(tom1) operator(=) ident(store)operator(.)ident(selectSingle)operator(()ident(Person)operator(.)ident(class)operator(,) string<delimiter(')content(name)delimiter(')>operator(,) ident(name1)(\))
+ident(tom2) operator(=) ident(store)operator(.)ident(selectSingle)operator(()ident(Person)operator(.)ident(class)operator(,) string<delimiter(')content(name)delimiter(')>operator(,) ident(name2)(\))
+
+ident(println) string<delimiter(")content(Two Toming: )inline<inline_delimiter($)ident(tom1)>content( )inline<inline_delimiter($)ident(tom2)>delimiter(")>
+
+keyword(if) operator(()ident(tom1)operator(.)ident(name) operator(==) ident(tom2)operator(.)ident(name) operator(&&) ident(tom1)operator(.)ident(does) operator(==) ident(tom2)operator(.)ident(does) operator(&&) ident(tom1)operator(.)ident(email) operator(==) ident(tom2)operator(.)ident(email)(\))
+ ident(println) string<delimiter(")content(You're having runtime fun with one Tom made two.)delimiter(")>
+keyword(else)
+ ident(println) string<delimiter(")content(No two Toms are ever alike)delimiter(")>
+
+ident(tom2)operator(.)ident(does) operator(=) string<delimiter(')content(Poet Programmer)delimiter(')>
+ident(store)operator(.)ident(save)operator(()ident(tom2)(\))
+comment(// =>)
+comment(// Two Toming: Person@12884e0 Person@8ab708)
+comment(// No two Toms are ever alike)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_14.9)
+comment(//----------------------------------------------------------------------------------)
+comment(// Use one of the mechanisms mentioned in 14.1 to load variables at the start)
+comment(// of the script and save them at the end. You can save the binding, individual)
+comment(// variables, maps of variables or composite objects.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_14.10)
+comment(//----------------------------------------------------------------------------------)
+keyword(import) include(groovy.sql.Sql)
+
+ident(users) operator(=) operator([)string<delimiter(')content(20)delimiter(')>operator(:)string<delimiter(')content(Joe Bloggs)delimiter(')>operator(,) string<delimiter(')content(40)delimiter(')>operator(:)string<delimiter(')content(Bill Clinton)delimiter(')>operator(,) string<delimiter(')content(60)delimiter(')>operator(:)string<delimiter(')content(Ben Franklin)delimiter(')>(])
+
+keyword(def) ident(source) operator(=) keyword(new) ident(org)operator(.)ident(hsqldb)operator(.)ident(jdbc)operator(.)ident(jdbcDataSource)operator(()(\))
+ident(source)operator(.)ident(database) operator(=) string<delimiter(')content(jdbc:hsqldb:mem:PLEAC)delimiter(')>
+ident(source)operator(.)ident(user) operator(=) string<delimiter(')content(sa)delimiter(')>
+ident(source)operator(.)ident(password) operator(=) string<delimiter(')delimiter(')>
+ident(db) operator(=) keyword(new) ident(Sql)operator(()ident(source)(\))
+
+ident(db)operator(.)ident(execute) string<delimiter(')content(CREATE TABLE users ( uid INT, login CHAR(8\) \);)delimiter(')>
+ident(users)operator(.)ident(each)operator({) ident(uid)operator(,) ident(login) operator(->)
+ ident(db)operator(.)ident(execute) string<delimiter(")content(INSERT INTO users ( uid, login \) VALUES ()inline<inline_delimiter($)ident(uid)>content(,)inline<inline_delimiter($)ident(login)>content(\);)delimiter(")>
+(})
+ident(db)operator(.)ident(eachRow)operator(()string<delimiter(')content(SELECT uid, login FROM users WHERE uid < 50)delimiter(')>(\))operator({)
+ ident(println) string<delimiter(")inline<inline_delimiter($)local_variable(it)>content(.uid )inline<inline_delimiter($)local_variable(it)>content(.login)delimiter(")>
+(})
+ident(db)operator(.)ident(execute) string<delimiter(')content(DROP TABLE users;)delimiter(')>
+comment(// =>)
+comment(// 20 Joe Bloggs)
+comment(// 40 Bill Clinton)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_14.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// variation to cookbook: uses Firefox instead of Netscape, always assumes)
+comment(// argument is a regex, has some others args, retains no args to list all)
+
+comment(// uses jmork mork dbm reading library:)
+comment(// http://www.smartwerkz.com/projects/jmork/index.html)
+keyword(import) include(mork.*)
+keyword(def) ident(cli) operator(=) keyword(new) ident(CliBuilder)operator(()(\))
+ident(cli)operator(.)ident(h)operator(()key(longOpt)operator(:) string<delimiter(')content(help)delimiter(')>operator(,) string<delimiter(')content(print this message)delimiter(')>(\))
+ident(cli)operator(.)ident(e)operator(()key(longOpt)operator(:) string<delimiter(')content(exclude)delimiter(')>operator(,) string<delimiter(')content(exclude hidden history entries (js, css, ads and images\))delimiter(')>(\))
+ident(cli)operator(.)ident(c)operator(()key(longOpt)operator(:) string<delimiter(')content(clean)delimiter(')>operator(,) string<delimiter(')content(clean off url query string when reporting urls)delimiter(')>(\))
+ident(cli)operator(.)ident(v)operator(()key(longOpt)operator(:) string<delimiter(')content(verbose)delimiter(')>operator(,) string<delimiter(')content(show referrer and first visit date)delimiter(')>(\))
+keyword(def) ident(options) operator(=) ident(cli)operator(.)ident(parse)operator(()ident(args)(\))
+keyword(if) operator(()ident(options)operator(.)ident(h)(\)) operator({) ident(cli)operator(.)ident(usage)operator(()(\))operator(;) pre_type(System)operator(.)ident(exit)operator(()integer(0)(\)) (})
+ident(regex) operator(=) ident(options)operator(.)ident(arguments)operator(()(\))
+keyword(if) operator(()ident(regex)(\)) ident(regex) operator(=) ident(regex)operator([)integer(0)(])
+ident(reader) operator(=) keyword(new) pre_type(FileReader)operator(()string<delimiter(')content(Pleac/data/history.dat)delimiter(')>(\))
+ident(morkDocument) operator(=) keyword(new) ident(MorkDocument)operator(()ident(reader)(\))
+ident(tables) operator(=) ident(morkDocument)operator(.)ident(tables)
+ident(tables)operator(.)ident(each)operator({) ident(table) operator(->)
+ ident(table)operator(.)ident(rows)operator(.)ident(each) operator({) ident(row) operator(->)
+ ident(url) operator(=) ident(row)operator(.)ident(getValue)operator(()string<delimiter(')content(URL)delimiter(')>(\))
+ keyword(if) operator(()ident(options)operator(.)ident(c)(\)) ident(url) operator(=) ident(url)operator(.)ident(tokenize)operator(()string<delimiter(')content(?)delimiter(')>(\))operator([)integer(0)(])
+ keyword(if) operator(()operator(!)ident(regex) operator(||) ident(url) operator(=~) ident(regex)(\)) operator({)
+ keyword(if) operator(()operator(!)ident(options)operator(.)ident(e) operator(||) ident(row)operator(.)ident(getValue)operator(()string<delimiter(')content(Hidden)delimiter(')>(\)) operator(!=) string<delimiter(')content(1)delimiter(')>(\)) operator({)
+ ident(println) string<delimiter(")inline<inline_delimiter($)ident(url)>char(\\n)content( Last Visited: )inline<inline_delimiter(${)ident(date(row,'LastVisitDate'\))inline_delimiter(})>delimiter(")>
+ keyword(if) operator(()ident(options)operator(.)ident(v)(\)) operator({)
+ ident(println) string<delimiter(")content( First Visited: )inline<inline_delimiter(${)ident(date(row,'FirstVisitDate'\))inline_delimiter(})>delimiter(")>
+ ident(println) string<delimiter(")content( Referrer: )inline<inline_delimiter(${)ident(row.getValue('Referrer'\))inline_delimiter(})>delimiter(")>
+ (})
+ (})
+ (})
+ (})
+(})
+keyword(def) method(date)operator(()ident(row)operator(,) ident(key)(\)) operator({)
+ keyword(return) keyword(new) pre_type(Date)operator(()operator(()type(long)(\))operator(()ident(row)operator(.)ident(getValue)operator(()ident(key)(\))operator(.)ident(toLong)operator(()(\))operator(/)integer(1000)(\)\))
+(})
+comment(// $ groovy gfh -ev oracle' =>)
+comment(// http://www.oracle.com/technology/products/jdev/index.html)
+comment(// Last Visited: Thu Feb 15 20:20:36 EST 2007)
+comment(// First Visited: Thu Feb 15 20:20:36 EST 2007)
+comment(// Referrer: http://docs.codehaus.org/display/GROOVY/Oracle+JDeveloper+Plugin)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.1)
+comment(//----------------------------------------------------------------------------------)
+comment(// The are several Java options builder packages available. Some popular ones:)
+comment(// Apache Jakarta Commons CLI: http://jakarta.apache.org/commons/cli/)
+comment(// jopt-simple: http://jopt-simple.sourceforge.net)
+comment(// args4j: https://args4j.dev.java.net/ (requires Java 5 with annotations\))
+comment(// jargs: http://jargs.sourceforge.net/)
+comment(// te-code: http://te-code.sourceforge.net/article-20041121-cli.html)
+comment(// Most of these can be used from Groovy with some Groovy code benefits.)
+comment(// Groovy also has the CliBuilder built right in.)
+
+
+comment(// CliBuilder example)
+keyword(def) ident(cli) operator(=) keyword(new) ident(CliBuilder)operator(()(\))
+ident(cli)operator(.)ident(v)operator(()key(longOpt)operator(:) string<delimiter(')content(verbose)delimiter(')>operator(,) string<delimiter(')content(verbose mode)delimiter(')>(\))
+ident(cli)operator(.)ident(D)operator(()key(longOpt)operator(:) string<delimiter(')content(Debug)delimiter(')>operator(,) string<delimiter(')content(display debug info)delimiter(')>(\))
+ident(cli)operator(.)ident(o)operator(()key(longOpt)operator(:) string<delimiter(')content(output)delimiter(')>operator(,) string<delimiter(')content(use/specify output file)delimiter(')>(\))
+keyword(def) ident(options) operator(=) ident(cli)operator(.)ident(parse)operator(()ident(args)(\))
+keyword(if) operator(()ident(options)operator(.)ident(v)(\)) comment(// ...)
+keyword(if) operator(()ident(options)operator(.)ident(D)(\)) ident(println) string<delimiter(')content(Debugging info available)delimiter(')>
+keyword(if) operator(()ident(options)operator(.)ident(o)(\)) operator({)
+ ident(println) string<delimiter(')content(Output file flag was specified)delimiter(')>
+ ident(println) string<delimiter(")content(Output file is )inline<inline_delimiter(${)ident(options.o)inline_delimiter(})>delimiter(")>
+(})
+comment(// ...)
+
+
+comment(// jopt-simple example 1 (short form\))
+ident(cli) operator(=) keyword(new) ident(joptsimple)operator(.)ident(OptionParser)operator(()string<delimiter(")content(vDo::)delimiter(")>(\))
+ident(options) operator(=) ident(cli)operator(.)ident(parse)operator(()ident(args)(\))
+keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(()string<delimiter(')content(o)delimiter(')>(\)\)) operator({)
+ ident(println) string<delimiter(')content(Output file flag was specified.)delimiter(')>
+ ident(println) string<delimiter(")content(Output file is )inline<inline_delimiter(${)ident(options.argumentsOf('o'\))inline_delimiter(})>delimiter(")>
+(})
+comment(// ...)
+
+
+comment(// jopt-simple example 2 (declarative form\))
+ident(op) operator(=) keyword(new) ident(joptsimple)operator(.)ident(OptionParser)operator(()(\))
+ident(VERBOSE) operator(=) string<delimiter(')content(v)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(VERBOSE)operator(,) string<delimiter(")content(verbose mode)delimiter(")> (\))
+ident(DEBUG) operator(=) string<delimiter(')content(D)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(DEBUG)operator(,) string<delimiter(")content(display debug info)delimiter(")> (\))
+ident(OUTPUT) operator(=) string<delimiter(')content(o)delimiter(')>operator(;) ident(op)operator(.)ident(accepts)operator(() ident(OUTPUT)operator(,) string<delimiter(")content(use/specify output file)delimiter(")> (\))operator(.)ident(withOptionalArg)operator(()(\))operator(.)
+ ident(describedAs)operator(() string<delimiter(")content(file)delimiter(")> (\))operator(.)ident(ofType)operator(() pre_type(File)operator(.)ident(class) (\))
+ident(options) operator(=) ident(op)operator(.)ident(parse)operator(()ident(args)(\))
+ident(params) operator(=) ident(options)operator(.)ident(nonOptionArguments)operator(()(\))
+keyword(if) operator(()ident(options)operator(.)ident(wasDetected)operator(() ident(DEBUG) (\)\)) ident(println) string<delimiter(')content(Debugging info available)delimiter(')>
+comment(// ...)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy like Java can be run in a variety of scenarios, not just interactive vs)
+comment(// non-interative, e.g. within a servlet container. Sometimes InputStreams and other)
+comment(// mechanisms are used to hide away differences between the different containers)
+comment(// in which code is run; other times, code needs to be written purpose-built for)
+comment(// the container in which it is running. In most situations where the latter applies)
+comment(// the container will have specific lifecycle mechanisms to allow the code to)
+comment(// access specific needs, e.g. javax.servlet.ServletRequest.getInputStream(\))
+comment(// rather than System.in)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// Idiomatically Groovy encourages GUI over text-based applications where a rich)
+comment(// interface is desirable. Libraries for richer text-based interfaces include:)
+comment(// jline: http://jline.sourceforge.net)
+comment(// jcurses: http://sourceforge.net/projects/javacurses/)
+comment(// java-readline: http://java-readline.sourceforge.net)
+comment(// enigma console: http://sourceforge.net/projects/enigma-shell/)
+comment(// Note: Run examples using these libraries from command line not inside an IDE.)
+
+comment(// If you are using a terminal/console that understands ANSI codes)
+comment(// (excludes WinNT derivatives\) you can just print the ANSI codes)
+ident(print) operator(()operator(()type(char)(\))integer(27) operator(+) string<delimiter(')content([2J)delimiter(')>(\))
+
+comment(// jline has constants for ANSI codes)
+keyword(import) include(jline.ANSIBuffer)
+ident(print) ident(ANSIBuffer)operator(.)ident(ANSICodes)operator(.)ident(clrscr)operator(()(\))
+comment(// Also available through ConsoleReader.clearScreen(\))
+
+comment(// Using jcurses)
+keyword(import) include(jcurses.system.*)
+ident(bg) operator(=) ident(CharColor)operator(.)ident(BLACK)
+ident(fg) operator(=) ident(CharColor)operator(.)ident(WHITE)
+ident(screenColors) operator(=) keyword(new) ident(CharColor)operator(()ident(bg)operator(,) ident(fg)(\))
+pre_type(Toolkit)operator(.)ident(clearScreen)operator(()ident(screenColors)(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// Not idiomatic for Groovy to use text-based applications here.)
+
+comment(// Using jcurses: http://sourceforge.net/projects/javacurses/)
+comment(// use Toolkit.screenWidth and Toolkit.screenHeight)
+
+comment(// 'barchart' example)
+keyword(import) include(jcurses.system.Toolkit)
+ident(numCols) operator(=) pre_type(Toolkit)operator(.)ident(screenWidth)
+ident(rand) operator(=) keyword(new) pre_type(Random)operator(()(\))
+keyword(if) operator(()ident(numCols) operator(<) integer(20)(\)) keyword(throw) keyword(new) pre_type(RuntimeException)operator(()string<delimiter(")content(You must have at least 20 characters)delimiter(")>(\))
+ident(values) operator(=) operator(()integer(1)operator(..)integer(5)(\))operator(.)ident(collect) operator({) ident(rand)operator(.)ident(nextInt)operator(()integer(20)(\)) (}) comment(// generate rand values)
+ident(max) operator(=) ident(values)operator(.)ident(max)operator(()(\))
+ident(ratio) operator(=) operator(()ident(numCols) operator(-) integer(12)(\))operator(/)ident(max)
+ident(values)operator(.)ident(each)operator({) ident(i) operator(->)
+ ident(printf)operator(()string<delimiter(')content(%8.1f %s)content(\\n)delimiter(')>operator(,) operator([)ident(i) keyword(as) type(double)operator(,) string<delimiter(")content(*)delimiter(")> operator(*) ident(ratio) operator(*) ident(i)(]\))
+(})
+
+comment(// gives, for example:)
+comment(// 15.0 *******************************)
+comment(// 10.0 *********************)
+comment(// 5.0 **********)
+comment(// 14.0 *****************************)
+comment(// 18.0 **************************************)
+comment(// Run from command line not inside an IDE which may give false width/height values.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// Idiomatically Groovy encourages GUI over text-based applications where a rich)
+comment(// interface is desirable. See 15.3 for richer text-based interface libraries.)
+comment(// Note: Run examples using these libraries from command line not inside an IDE.)
+
+comment(// If you are using a terminal/console that understands ANSI codes)
+comment(// (excludes WinNT derivatives\) you can just print the ANSI codes)
+ident(ESC) operator(=) string<delimiter(")inline<inline_delimiter(${)ident((char\)27)inline_delimiter(})>delimiter(")>
+ident(redOnBlack) operator(=) ident(ESC) operator(+) string<delimiter(')content([31;40m)delimiter(')>
+ident(reset) operator(=) ident(ESC) operator(+) string<delimiter(')content([0m)delimiter(')>
+ident(println) operator(()ident(redOnBlack) operator(+) string<delimiter(')content(Danger, Will Robinson!)delimiter(')> operator(+) ident(reset)(\))
+
+comment(// jline has constants for ANSI codes)
+keyword(import) include(jline.ANSIBuffer)
+ident(redOnBlack) operator(=) ident(ANSIBuffer)operator(.)ident(ANSICodes)operator(.)ident(attrib)operator(()integer(31)(\)) operator(+) ident(ANSIBuffer)operator(.)ident(ANSICodes)operator(.)ident(attrib)operator(()integer(40)(\))
+ident(reset) operator(=) ident(ANSIBuffer)operator(.)ident(ANSICodes)operator(.)ident(attrib)operator(()integer(0)(\))
+ident(println) ident(redOnBlack) operator(+) string<delimiter(')content(Danger, Will Robinson!)delimiter(')> operator(+) ident(reset)
+
+comment(// Using JavaCurses)
+keyword(import) include(jcurses.system.*)
+keyword(import) include(jcurses.widgets.*)
+ident(whiteOnBlack) operator(=) keyword(new) ident(CharColor)operator(()ident(CharColor)operator(.)ident(BLACK)operator(,) ident(CharColor)operator(.)ident(WHITE)(\))
+pre_type(Toolkit)operator(.)ident(clearScreen)operator(()ident(whiteOnBlack)(\))
+ident(redOnBlack) operator(=) keyword(new) ident(CharColor)operator(()ident(CharColor)operator(.)ident(BLACK)operator(,) ident(CharColor)operator(.)ident(RED)(\))
+pre_type(Toolkit)operator(.)ident(printString)operator(()string<delimiter(")content(Danger, Will Robinson!)delimiter(")>operator(,) integer(0)operator(,) integer(0)operator(,) ident(redOnBlack)(\))
+pre_type(Toolkit)operator(.)ident(printString)operator(()string<delimiter(")content(This is just normal text.)delimiter(")>operator(,) integer(0)operator(,) integer(1)operator(,) ident(whiteOnBlack)(\))
+comment(// Blink not supported by JavaCurses)
+
+comment(// Using jline constants for Blink)
+ident(blink) operator(=) ident(ANSIBuffer)operator(.)ident(ANSICodes)operator(.)ident(attrib)operator(()integer(5)(\))
+ident(reset) operator(=) ident(ANSIBuffer)operator(.)ident(ANSICodes)operator(.)ident(attrib)operator(()integer(0)(\))
+ident(println) operator(()ident(blink) operator(+) string<delimiter(')content(Do you hurt yet?)delimiter(')> operator(+) ident(reset)(\))
+
+comment(// Using jline constants for Coral snake rhyme)
+keyword(def) method(ansi)operator(()ident(code)(\)) operator({) ident(ANSIBuffer)operator(.)ident(ANSICodes)operator(.)ident(attrib)operator(()ident(code)(\)) (})
+ident(redOnBlack) operator(=) ident(ansi)operator(()integer(31)(\)) operator(+) ident(ansi)operator(()integer(40)(\))
+ident(redOnYellow) operator(=) ident(ansi)operator(()integer(31)(\)) operator(+) ident(ansi)operator(()integer(43)(\))
+ident(greenOnCyanBlink) operator(=) ident(ansi)operator(()integer(32)(\)) operator(+) ident(ansi)operator(()integer(46)(\)) operator(+) ident(ansi)operator(()integer(5)(\))
+ident(reset) operator(=) ident(ansi)operator(()integer(0)(\))
+ident(println) ident(redOnBlack) operator(+) string<delimiter(")content(venom lack)delimiter(")>
+ident(println) ident(redOnYellow) operator(+) string<delimiter(")content(kill that fellow)delimiter(")>
+ident(println) ident(greenOnCyanBlink) operator(+) string<delimiter(")content(garish!)delimiter(")> operator(+) ident(reset)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// Default Java libraries buffer System.in by default.)
+
+comment(// Using JavaCurses:)
+keyword(import) include(jcurses.system.Toolkit)
+ident(print) string<delimiter(')content(Press a key: )delimiter(')>
+ident(println) string<delimiter(")char(\\n)content(You pressed the ')inline<inline_delimiter(${)ident(Toolkit.readCharacter(\).character)inline_delimiter(})>content(' key)delimiter(")>
+
+comment(// Also works for special keys:)
+keyword(import) include(jcurses.system.InputChar)
+ident(print) string<delimiter(")content(Press the 'End' key to finish: )delimiter(")>
+ident(ch) operator(=) pre_type(Toolkit)operator(.)ident(readCharacter)operator(()(\))
+keyword(assert) ident(ch)operator(.)ident(isSpecialCode)operator(()(\))
+keyword(assert) ident(ch)operator(.)ident(code) operator(==) ident(InputChar)operator(.)ident(KEY_END)
+
+comment(// See also jline Terminal#readCharacter(\) and Terminal#readVirtualKey(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.7)
+comment(//----------------------------------------------------------------------------------)
+ident(print) string<delimiter(")inline<inline_delimiter(${)ident((char\)7)inline_delimiter(})>delimiter(")>
+
+comment(// Using jline constant)
+ident(print) string<delimiter(")inline<inline_delimiter(${)ident(jline.ConsoleOperations.KEYBOARD_BELL)inline_delimiter(})>delimiter(")>
+comment(// Also available through ConsoleReader.beep(\))
+
+comment(// Using JavaCurses (Works only with terminals that support 'beeps'\))
+keyword(import) include(jcurses.system.Toolkit)
+pre_type(Toolkit)operator(.)ident(beep)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.8)
+comment(//----------------------------------------------------------------------------------)
+comment(// I think you would need to resort to platform specific calls here,)
+comment(// E.g. on *nix systems call 'stty' using execute(\).)
+comment(// Some things can be set through the packages mentioned in 15.3, e.g.)
+comment(// echo can be turned on and off, but others like setting the kill character)
+comment(// didn't appear to be supported (presumably because it doesn't make)
+comment(// sense for a cross-platform toolkit\).)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.9)
+comment(//----------------------------------------------------------------------------------)
+comment(// Consider using Java's PushbackInputStream or PushbackReader)
+comment(// Different functionality to original cookbook but can be used)
+comment(// as an alternative for some scenarios.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.10)
+comment(//----------------------------------------------------------------------------------)
+comment(// If using Java 6, use Console.readPassword(\))
+comment(// Otherwise use jline (use 0 instead of mask character '*' for no echo\):)
+ident(password) operator(=) keyword(new) ident(jline)operator(.)ident(ConsoleReader)operator(()(\))operator(.)ident(readLine)operator(()keyword(new) pre_type(Character)operator(()string<delimiter(')content(*)delimiter(')>(\)\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// In Groovy (like Java\) normal input is buffered so you can normally make)
+comment(// edits before hitting 'Enter'. For more control over editing (including completion)
+comment(// and history etc.\) use one of the packages mentioned in 15.3, e.g. jline.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.12)
+comment(//----------------------------------------------------------------------------------)
+comment(// Use javacurses or jline (see 15.3\) for low level screen management.)
+comment(// Java/Groovy would normally use a GUI for such functionality.)
+
+comment(// Here is a slight variation to cookbook example. This repeatedly calls)
+comment(// the command feedin on the command line, e.g. "cmd /c dir" on windows)
+comment(// or 'ps -aux' on Linux. Whenever a line changes, the old line is "faded)
+comment(// out" using font colors from white through to black. Then the new line)
+comment(// is faded in using the reverse process.)
+keyword(import) include(jcurses.system.*)
+ident(color) operator(=) keyword(new) ident(CharColor)operator(()ident(CharColor)operator(.)ident(BLACK)operator(,) ident(CharColor)operator(.)ident(WHITE)(\))
+pre_type(Toolkit)operator(.)ident(clearScreen)operator(()ident(color)(\))
+ident(maxcol) operator(=) pre_type(Toolkit)operator(.)ident(screenWidth)
+ident(maxrow) operator(=) pre_type(Toolkit)operator(.)ident(screenHeight)
+ident(colors) operator(=) operator([)ident(CharColor)operator(.)ident(WHITE)operator(,) ident(CharColor)operator(.)ident(CYAN)operator(,) ident(CharColor)operator(.)ident(YELLOW)operator(,) ident(CharColor)operator(.)ident(GREEN)operator(,)
+ ident(CharColor)operator(.)ident(RED)operator(,) ident(CharColor)operator(.)ident(BLUE)operator(,) ident(CharColor)operator(.)ident(MAGENTA)operator(,) ident(CharColor)operator(.)ident(BLACK)(])
+ident(done) operator(=) keyword(false)
+ident(refresh) operator(=) keyword(false)
+ident(waittime) operator(=) integer(8000)
+ident(oldlines) operator(=) type([])
+keyword(def) method(fade)operator(()ident(line)operator(,) ident(row)operator(,) ident(colorList)(\)) operator({)
+ keyword(for) operator(()ident(i) keyword(in) integer(0)operator(..<)ident(colorList)operator(.)ident(size)operator(()(\)\)) operator({)
+ pre_type(Toolkit)operator(.)ident(printString)operator(()ident(line)operator(,) integer(0)operator(,) ident(row)operator(,) keyword(new) ident(CharColor)operator(()ident(CharColor)operator(.)ident(BLACK)operator(,) ident(colorList)operator([)ident(i)(]\)\))
+ ident(sleep) integer(10)
+ (})
+(})
+keyword(while)operator(()operator(!)ident(done)(\)) operator({)
+ keyword(if) operator(()ident(waittime) operator(>) integer(9999) operator(||) ident(refresh)(\)) operator({)
+ ident(proc) operator(=) ident(args)operator([)integer(0)(])operator(.)ident(execute)operator(()(\))
+ ident(lines) operator(=) ident(proc)operator(.)ident(text)operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+ keyword(for) operator(()ident(r) keyword(in) integer(0)operator(..<)ident(maxrow)(\)) operator({)
+ keyword(if) operator(()ident(r) operator(>=) ident(lines)operator(.)ident(size)operator(()(\)) operator(||) ident(r) operator(>) ident(oldlines)operator(.)ident(size)operator(()(\)) operator(||) ident(lines)operator([)ident(r)(]) operator(!=) ident(oldlines)operator([)ident(r)(]\)) operator({)
+ keyword(if) operator(()ident(oldlines) operator(!=) type([])(\))
+ ident(fade)operator(()ident(r) operator(<) ident(oldlines)operator(.)ident(size)operator(()(\)) operator(?) ident(oldlines)operator([)ident(r)(]) operator(:) string<delimiter(')content( )delimiter(')> operator(*) ident(maxcol)operator(,) ident(r)operator(,) ident(colors)(\))
+ ident(fade)operator(()ident(r) operator(<) ident(lines)operator(.)ident(size)operator(()(\)) operator(?) ident(lines)operator([)ident(r)(]) operator(:) string<delimiter(')content( )delimiter(')> operator(*) ident(maxcol)operator(,) ident(r)operator(,) ident(colors)operator(.)ident(reverse)operator(()(\)\))
+ (})
+ (})
+ ident(oldlines) operator(=) ident(lines)
+ ident(refresh) operator(=) keyword(false)
+ ident(waittime) operator(=) integer(0)
+ (})
+ ident(waittime) operator(+=) integer(200)
+ ident(sleep) integer(200)
+(})
+
+comment(// Keyboard handling would be similar to 15.6.)
+comment(// Something like below but need to synchronize as we are in different threads.)
+pre_type(Thread)operator(.)ident(start)operator({)
+ keyword(while)operator(()operator(!)ident(done)(\)) operator({)
+ ident(ch) operator(=) pre_type(Toolkit)operator(.)ident(readCharacter)operator(()(\))
+ keyword(if) operator(()ident(ch)operator(.)ident(isSpecialCode)operator(()(\)) operator(||) ident(ch)operator(.)ident(character) operator(==) string<delimiter(')content(q)delimiter(')>(\)) ident(done) operator(=) keyword(true)
+ keyword(else) ident(refresh) operator(=) keyword(true)
+ (})
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// These examples uses expectj, a pure Java Expect-like module.)
+comment(// http://expectj.sourceforge.net/)
+ident(defaultTimeout) operator(=) operator(-)integer(1) comment(// infinite)
+ident(expect) operator(=) keyword(new) ident(expectj)operator(.)ident(ExpectJ)operator(()string<delimiter(")content(logfile.log)delimiter(")>operator(,) ident(defaultTimeout)(\))
+ident(command) operator(=) ident(expect)operator(.)ident(spawn)operator(()string<delimiter(")content(program to run)delimiter(")>(\))
+ident(command)operator(.)ident(expect)operator(()string<delimiter(')content(Password)delimiter(')>operator(,) integer(10)(\))
+comment(// expectj doesn't support regular expressions, but see readUntil)
+comment(// in recipe 18.6 for how to manually code this)
+ident(command)operator(.)ident(expect)operator(()string<delimiter(')content(invalid)delimiter(')>(\))
+ident(command)operator(.)ident(send)operator(()string<delimiter(')content(Hello, world)content(\\r)delimiter(')>(\))
+comment(// kill spawned process)
+ident(command)operator(.)ident(stop)operator(()(\))
+
+comment(// expecting multiple choices)
+comment(// expectj doesn't support multiple choices, but see readUntil)
+comment(// in recipe 18.6 for how to manually code this)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.14)
+comment(//----------------------------------------------------------------------------------)
+comment(// Methods not shown for the edit menu items, they would be the same as for the)
+comment(// file menu items.)
+keyword(import) include(groovy.swing.SwingBuilder)
+keyword(def) method(print)operator(()(\)) operator({)(})
+keyword(def) method(save)operator(()(\)) operator({)(})
+ident(frame) operator(=) keyword(new) ident(SwingBuilder)operator(()(\))operator(.)ident(frame)operator(()key(title)operator(:)string<delimiter(')content(Demo)delimiter(')>(\)) operator({)
+ ident(menuBar) operator({)
+ ident(menu)operator(()key(mnemonic)operator(:)string<delimiter(')content(F)delimiter(')>operator(,) string<delimiter(')content(File)delimiter(')>(\)) operator({)
+ ident(menuItem) operator(()key(actionPerformed)operator(:)local_variable(this)operator(.)operator(&)ident(print)operator(,) string<delimiter(')content(Print)delimiter(')>(\))
+ ident(separator)operator(()(\))
+ ident(menuItem) operator(()key(actionPerformed)operator(:)local_variable(this)operator(.)operator(&)ident(save)operator(,) string<delimiter(')content(Save)delimiter(')>(\))
+ ident(menuItem) operator(()key(actionPerformed)operator(:)operator({)pre_type(System)operator(.)ident(exit)operator(()integer(0)(\)})operator(,) string<delimiter(')content(Quit immediately)delimiter(')>(\))
+ (})
+ ident(menu)operator(()key(mnemonic)operator(:)string<delimiter(')content(O)delimiter(')>operator(,) string<delimiter(')content(Options)delimiter(')>(\)) operator({)
+ ident(checkBoxMenuItem) operator(()string<delimiter(')content(Create Debugging Info)delimiter(')>operator(,) key(state)operator(:)keyword(true)(\))
+ (})
+ ident(menu)operator(()key(mnemonic)operator(:)string<delimiter(')content(D)delimiter(')>operator(,) string<delimiter(')content(Debug)delimiter(')>(\)) operator({)
+ ident(group) operator(=) ident(buttonGroup)operator(()(\))
+ ident(radioButtonMenuItem) operator(()string<delimiter(')content(Log Level 1)delimiter(')>operator(,) key(buttonGroup)operator(:)ident(group)operator(,) key(selected)operator(:)keyword(true)(\))
+ ident(radioButtonMenuItem) operator(()string<delimiter(')content(Log Level 2)delimiter(')>operator(,) key(buttonGroup)operator(:)ident(group)(\))
+ ident(radioButtonMenuItem) operator(()string<delimiter(')content(Log Level 3)delimiter(')>operator(,) key(buttonGroup)operator(:)ident(group)(\))
+ (})
+ ident(menu)operator(()key(mnemonic)operator(:)string<delimiter(')content(F)delimiter(')>operator(,) string<delimiter(')content(Format)delimiter(')>(\)) operator({)
+ ident(menu)operator(()string<delimiter(')content(Font)delimiter(')>(\)) operator({)
+ ident(group) operator(=) ident(buttonGroup)operator(()(\))
+ ident(radioButtonMenuItem) operator(()string<delimiter(')content(Times Roman)delimiter(')>operator(,) key(buttonGroup)operator(:)ident(group)operator(,) key(selected)operator(:)keyword(true)(\))
+ ident(radioButtonMenuItem) operator(()string<delimiter(')content(Courier)delimiter(')>operator(,) key(buttonGroup)operator(:)ident(group)(\))
+ (})
+ (})
+ ident(menu)operator(()key(mnemonic)operator(:)string<delimiter(')content(E)delimiter(')>operator(,) string<delimiter(')content(Edit)delimiter(')>(\)) operator({)
+ ident(menuItem) operator(()key(actionPerformed)operator(:)operator({)(})operator(,) string<delimiter(')content(Copy)delimiter(')>(\))
+ ident(menuItem) operator(()key(actionPerformed)operator(:)operator({)(})operator(,) string<delimiter(')content(Cut)delimiter(')>(\))
+ ident(menuItem) operator(()key(actionPerformed)operator(:)operator({)(})operator(,) string<delimiter(')content(Paste)delimiter(')>(\))
+ ident(menuItem) operator(()key(actionPerformed)operator(:)operator({)(})operator(,) string<delimiter(')content(Delete)delimiter(')>(\))
+ ident(separator)operator(()(\))
+ ident(menu)operator(()string<delimiter(')content(Object ...)delimiter(')>(\)) operator({)
+ ident(menuItem) operator(()key(actionPerformed)operator(:)operator({)(})operator(,) string<delimiter(')content(Circle)delimiter(')>(\))
+ ident(menuItem) operator(()key(actionPerformed)operator(:)operator({)(})operator(,) string<delimiter(')content(Square)delimiter(')>(\))
+ ident(menuItem) operator(()key(actionPerformed)operator(:)operator({)(})operator(,) string<delimiter(')content(Point)delimiter(')>(\))
+ (})
+ (})
+ (})
+(})
+ident(frame)operator(.)ident(pack)operator(()(\))
+ident(frame)operator(.)ident(show)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.15)
+comment(//----------------------------------------------------------------------------------)
+comment(// Registration Example)
+keyword(import) include(groovy.swing.SwingBuilder)
+keyword(def) method(cancel)operator(()ident(event)(\)) operator({)
+ ident(println) string<delimiter(')content(Sorry you decided not to register.)delimiter(')>
+ ident(dialog)operator(.)ident(dispose)operator(()(\))
+(})
+keyword(def) method(register)operator(()ident(event)(\)) operator({)
+ keyword(if) operator(()ident(swing)operator(.)ident(name)operator(?)operator(.)ident(text)(\)) operator({)
+ ident(println) string<delimiter(")content(Welcome to the fold )inline<inline_delimiter($)ident(swing)>content(.name.text)delimiter(")>
+ ident(dialog)operator(.)ident(dispose)operator(()(\))
+ (}) keyword(else) ident(println) string<delimiter(")content(You didn't give me your name!)delimiter(")>
+(})
+keyword(def) method(dialog)operator(()ident(event)(\)) operator({)
+ ident(dialog) operator(=) ident(swing)operator(.)ident(createDialog)operator(()key(title)operator(:)string<delimiter(')content(Entry)delimiter(')>(\))
+ keyword(def) ident(panel) operator(=) ident(swing)operator(.)ident(panel) operator({)
+ ident(vbox) operator({)
+ ident(hbox) operator({)
+ ident(label)operator(()key(text)operator(:)string<delimiter(')content(Name)delimiter(')>(\))
+ ident(textField)operator(()key(columns)operator(:)integer(20)operator(,) key(id)operator(:)string<delimiter(')content(name)delimiter(')>(\))
+ (})
+ ident(hbox) operator({)
+ ident(button)operator(()string<delimiter(')content(Register)delimiter(')>operator(,) key(actionPerformed)operator(:)local_variable(this)operator(.)operator(&)ident(register)(\))
+ ident(button)operator(()string<delimiter(')content(Cancel)delimiter(')>operator(,) key(actionPerformed)operator(:)local_variable(this)operator(.)operator(&)ident(cancel)(\))
+ (})
+ (})
+ (})
+ ident(dialog)operator(.)ident(getContentPane)operator(()(\))operator(.)ident(add)operator(()ident(panel)(\))
+ ident(dialog)operator(.)ident(pack)operator(()(\))
+ ident(dialog)operator(.)ident(show)operator(()(\))
+(})
+ident(swing) operator(=) keyword(new) ident(SwingBuilder)operator(()(\))
+ident(frame) operator(=) ident(swing)operator(.)ident(frame)operator(()key(title)operator(:)string<delimiter(')content(Registration Example)delimiter(')>(\)) operator({)
+ ident(panel) operator({)
+ ident(button)operator(()key(actionPerformed)operator(:)local_variable(this)operator(.)operator(&)ident(dialog)operator(,) string<delimiter(')content(Click Here For Registration Form)delimiter(')>(\))
+ ident(glue)operator(()(\))
+ ident(button)operator(()key(actionPerformed)operator(:)operator({)pre_type(System)operator(.)ident(exit)operator(()integer(0)(\)})operator(,) string<delimiter(')content(Quit)delimiter(')>(\))
+ (})
+(})
+ident(frame)operator(.)ident(pack)operator(()(\))
+ident(frame)operator(.)ident(show)operator(()(\))
+
+
+comment(// Error Example, slight variation to original cookbook)
+keyword(import) include(groovy.swing.SwingBuilder)
+keyword(import) include(javax.swing.WindowConstants) keyword(as) class(WC)
+keyword(import) include(javax.swing.JOptionPane)
+keyword(def) method(calculate)operator(()ident(event)(\)) operator({)
+ keyword(try) operator({)
+ ident(swing)operator(.)ident(result)operator(.)ident(text) operator(=) ident(evaluate)operator(()ident(swing)operator(.)ident(expr)operator(.)ident(text)(\))
+ (}) keyword(catch) operator(()pre_type(Exception) ident(ex)(\)) operator({)
+ pre_type(JOptionPane)operator(.)ident(showMessageDialog)operator(()ident(frame)operator(,) ident(ex)operator(.)ident(message)(\))
+ (})
+(})
+ident(swing) operator(=) keyword(new) ident(SwingBuilder)operator(()(\))
+ident(frame) operator(=) ident(swing)operator(.)ident(frame)operator(()key(title)operator(:)string<delimiter(')content(Calculator Example)delimiter(')>operator(,)
+ key(defaultCloseOperation)operator(:)ident(WC)operator(.)ident(EXIT_ON_CLOSE)(\)) operator({)
+ ident(panel) operator({)
+ ident(vbox) operator({)
+ ident(hbox) operator({)
+ ident(label)operator(()key(text)operator(:)string<delimiter(')content(Expression)delimiter(')>(\))
+ ident(hstrut)operator(()(\))
+ ident(textField)operator(()key(columns)operator(:)integer(12)operator(,) key(id)operator(:)string<delimiter(')content(expr)delimiter(')>(\))
+ (})
+ ident(hbox) operator({)
+ ident(label)operator(()key(text)operator(:)string<delimiter(')content(Result)delimiter(')>(\))
+ ident(glue)operator(()(\))
+ ident(label)operator(()key(id)operator(:)string<delimiter(')content(result)delimiter(')>(\))
+ (})
+ ident(hbox) operator({)
+ ident(button)operator(()string<delimiter(')content(Calculate)delimiter(')>operator(,) key(actionPerformed)operator(:)local_variable(this)operator(.)operator(&)ident(calculate)(\))
+ ident(button)operator(()string<delimiter(')content(Quit)delimiter(')>operator(,) key(actionPerformed)operator(:)operator({)pre_type(System)operator(.)ident(exit)operator(()integer(0)(\)}\))
+ (})
+ (})
+ (})
+(})
+ident(frame)operator(.)ident(pack)operator(()(\))
+ident(frame)operator(.)ident(show)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.16)
+comment(//----------------------------------------------------------------------------------)
+comment(// Resizing in Groovy follows Java rules, i.e. is dependent on the layout manager.)
+comment(// You can set preferred, minimum and maximum sizes (may be ignored by some layout managers\).)
+comment(// You can setResizable(false\) for some components.)
+comment(// You can specify a weight value for some layout managers, e.g. GridBagLayout)
+comment(// which control the degree of scaling which occurs during resizing.)
+comment(// Some layout managers, e.g. GridLayout, automaticaly resize their contained widgets.)
+comment(// You can capture resize events and do everything manually yourself.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.17)
+comment(//----------------------------------------------------------------------------------)
+comment(// Removing DOS console on Windows:)
+comment(// If you are using java.exe to start your Groovy script, use javaw.exe instead.)
+comment(// If you are using groovy.exe to start your Groovy script, use groovyw.exe instead.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.18)
+comment(//----------------------------------------------------------------------------------)
+comment(// additions to original cookbook:)
+comment(// random starting position)
+comment(// color changes after each bounce)
+keyword(import) include(jcurses.system.*)
+ident(color) operator(=) keyword(new) ident(CharColor)operator(()ident(CharColor)operator(.)ident(BLACK)operator(,) ident(CharColor)operator(.)ident(WHITE)(\))
+pre_type(Toolkit)operator(.)ident(clearScreen)operator(()ident(color)(\))
+ident(rand) operator(=) keyword(new) pre_type(Random)operator(()(\))
+ident(maxrow) operator(=) pre_type(Toolkit)operator(.)ident(screenWidth)
+ident(maxcol) operator(=) pre_type(Toolkit)operator(.)ident(screenHeight)
+ident(rowinc) operator(=) integer(1)
+ident(colinc) operator(=) integer(1)
+ident(row) operator(=) ident(rand)operator(.)ident(nextInt)operator(()ident(maxrow)(\))
+ident(col) operator(=) ident(rand)operator(.)ident(nextInt)operator(()ident(maxcol)(\))
+ident(chars) operator(=) string<delimiter(')content(*-/|)char(\\\\)content(_)delimiter(')>
+ident(colors) operator(=) operator([)ident(CharColor)operator(.)ident(RED)operator(,) ident(CharColor)operator(.)ident(BLUE)operator(,) ident(CharColor)operator(.)ident(YELLOW)operator(,)
+ ident(CharColor)operator(.)ident(GREEN)operator(,) ident(CharColor)operator(.)ident(CYAN)operator(,) ident(CharColor)operator(.)ident(MAGENTA)(])
+ident(delay) operator(=) integer(20)
+ident(ch) operator(=) keyword(null)
+keyword(def) method(nextChar)operator(()(\))operator({)
+ ident(ch) operator(=) ident(chars)operator([)integer(0)(])
+ ident(chars) operator(=) ident(chars)operator([)integer(1)operator(..)operator(-)integer(1)(]) operator(+) ident(chars)operator([)integer(0)(])
+ ident(color) operator(=) keyword(new) ident(CharColor)operator(()ident(CharColor)operator(.)ident(BLACK)operator(,) ident(colors)operator([)integer(0)(]\))
+ ident(colors) operator(=) ident(colors)operator([)integer(1)operator(..)operator(-)integer(1)(]) operator(+) ident(colors)operator([)integer(0)(])
+(})
+ident(nextChar)operator(()(\))
+keyword(while)operator(()keyword(true)(\)) operator({)
+ pre_type(Toolkit)operator(.)ident(printString)operator(()ident(ch)operator(,) ident(row)operator(,) ident(col)operator(,) ident(color)(\))
+ ident(sleep) ident(delay)
+ ident(row) operator(=) ident(row) operator(+) ident(rowinc)
+ ident(col) operator(=) ident(col) operator(+) ident(colinc)
+ keyword(if) operator(()ident(row) keyword(in) operator([)integer(0)operator(,) ident(maxrow)(]\)) operator({) ident(nextChar)operator(()(\))operator(;) ident(rowinc) operator(=) operator(-)ident(rowinc) (})
+ keyword(if) operator(()ident(col) keyword(in) operator([)integer(0)operator(,) ident(maxcol)(]\)) operator({) ident(nextChar)operator(()(\))operator(;) ident(colinc) operator(=) operator(-)ident(colinc) (})
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_15.19)
+comment(//----------------------------------------------------------------------------------)
+comment(// Variation to cookbook. Let's you reshuffle lines in a multi-line string)
+comment(// by drag-n-drop.)
+keyword(import) include(java.awt.*)
+keyword(import) include(java.awt.datatransfer.*)
+keyword(import) include(java.awt.dnd.*)
+keyword(import) include(javax.swing.*)
+keyword(import) include(javax.swing.ScrollPaneConstants) keyword(as) class(SPC)
+
+type(class) class(DragDropList) directive(extends) pre_type(JList) directive(implements)
+ pre_type(DragSourceListener)operator(,) pre_type(DropTargetListener)operator(,) pre_type(DragGestureListener) operator({)
+ keyword(def) ident(dragSource)
+ keyword(def) ident(dropTarget)
+ keyword(def) ident(dropTargetCell)
+ type(int) ident(draggedIndex) operator(=) operator(-)integer(1)
+ keyword(def) ident(localDataFlavor) operator(=) keyword(new) pre_type(DataFlavor)operator(()pre_type(DataFlavor)operator(.)ident(javaJVMLocalObjectMimeType)(\))
+ keyword(def) ident(supportedFlavors) operator(=) operator([)ident(localDataFlavor)(]) keyword(as) pre_type(DataFlavor)type([])
+
+ directive(public) ident(DragDropList)operator(()ident(model)(\)) operator({)
+ local_variable(super)operator(()(\))
+ ident(setModel)operator(()ident(model)(\))
+ ident(setCellRenderer)operator(()keyword(new) ident(DragDropCellRenderer)operator(()local_variable(this)(\)\))
+ ident(dragSource) operator(=) keyword(new) pre_type(DragSource)operator(()(\))
+ ident(dragSource)operator(.)ident(createDefaultDragGestureRecognizer)operator(()local_variable(this)operator(,) pre_type(DnDConstants)operator(.)ident(ACTION_MOVE)operator(,) local_variable(this)(\))
+ ident(dropTarget) operator(=) keyword(new) pre_type(DropTarget)operator(()local_variable(this)operator(,) local_variable(this)(\))
+ (})
+
+ directive(public) type(void) ident(dragGestureRecognized)operator(()pre_type(DragGestureEvent) ident(dge)(\)) operator({)
+ type(int) ident(index) operator(=) ident(locationToIndex)operator(()ident(dge)operator(.)ident(dragOrigin)(\))
+ keyword(if) operator(()ident(index) operator(==) operator(-)integer(1) operator(||) ident(index) operator(==) ident(model)operator(.)ident(size)operator(()(\)) operator(-) integer(1)(\)) keyword(return)
+ keyword(def) ident(trans) operator(=) keyword(new) ident(CustomTransferable)operator(()ident(model)operator(.)ident(getElementAt)operator(()ident(index)(\))operator(,) local_variable(this)(\))
+ ident(draggedIndex) operator(=) ident(index)
+ ident(dragSource)operator(.)ident(startDrag)operator(()ident(dge)operator(,) pre_type(Cursor)operator(.)ident(defaultCursor)operator(,) ident(trans)operator(,) local_variable(this)(\))
+ (})
+
+ directive(public) type(void) ident(dragDropEnd)operator(()pre_type(DragSourceDropEvent) ident(dsde)(\)) operator({)
+ ident(dropTargetCell) operator(=) keyword(null)
+ ident(draggedIndex) operator(=) operator(-)integer(1)
+ ident(repaint)operator(()(\))
+ (})
+
+ directive(public) type(void) ident(dragEnter)operator(()pre_type(DragSourceDragEvent) ident(dsde)(\)) operator({) (})
+
+ directive(public) type(void) ident(dragExit)operator(()pre_type(DragSourceEvent) ident(dse)(\)) operator({) (})
+
+ directive(public) type(void) ident(dragOver)operator(()pre_type(DragSourceDragEvent) ident(dsde)(\)) operator({) (})
+
+ directive(public) type(void) ident(dropActionChanged)operator(()pre_type(DragSourceDragEvent) ident(dsde)(\)) operator({) (})
+
+ directive(public) type(void) ident(dropActionChanged)operator(()pre_type(DropTargetDragEvent) ident(dtde)(\)) operator({) (})
+
+ directive(public) type(void) ident(dragExit)operator(()pre_type(DropTargetEvent) ident(dte)(\)) operator({) (})
+
+ directive(public) type(void) ident(dragEnter)operator(()pre_type(DropTargetDragEvent) ident(dtde)(\)) operator({)
+ keyword(if) operator(()ident(dtde)operator(.)ident(source) operator(!=) ident(dropTarget)(\)) ident(dtde)operator(.)ident(rejectDrag)operator(()(\))
+ keyword(else) ident(dtde)operator(.)ident(acceptDrag)operator(()pre_type(DnDConstants)operator(.)ident(ACTION_COPY_OR_MOVE)(\))
+ (})
+
+ directive(public) type(void) ident(dragOver)operator(()pre_type(DropTargetDragEvent) ident(dtde)(\)) operator({)
+ keyword(if) operator(()ident(dtde)operator(.)ident(source) operator(!=) ident(dropTarget)(\)) ident(dtde)operator(.)ident(rejectDrag)operator(()(\))
+ type(int) ident(index) operator(=) ident(locationToIndex)operator(()ident(dtde)operator(.)ident(location)(\))
+ keyword(if) operator(()ident(index) operator(==) operator(-)integer(1) operator(||) ident(index) operator(==) ident(draggedIndex) operator(+) integer(1)(\)) ident(dropTargetCell) operator(=) keyword(null)
+ keyword(else) ident(dropTargetCell) operator(=) ident(model)operator(.)ident(getElementAt)operator(()ident(index)(\))
+ ident(repaint)operator(()(\))
+ (})
+
+ directive(public) type(void) ident(drop)operator(()pre_type(DropTargetDropEvent) ident(dtde)(\)) operator({)
+ keyword(if) operator(()ident(dtde)operator(.)ident(source) operator(!=) ident(dropTarget)(\)) operator({)
+ ident(dtde)operator(.)ident(rejectDrop)operator(()(\))
+ keyword(return)
+ (})
+ type(int) ident(index) operator(=) ident(locationToIndex)operator(()ident(dtde)operator(.)ident(location)(\))
+ keyword(if) operator(()ident(index) operator(==) operator(-)integer(1) operator(||) ident(index) operator(==) ident(draggedIndex)(\)) operator({)
+ ident(dtde)operator(.)ident(rejectDrop)operator(()(\))
+ keyword(return)
+ (})
+ ident(dtde)operator(.)ident(acceptDrop)operator(()pre_type(DnDConstants)operator(.)ident(ACTION_MOVE)(\))
+ keyword(def) ident(dragged) operator(=) ident(dtde)operator(.)ident(transferable)operator(.)ident(getTransferData)operator(()ident(localDataFlavor)(\))
+ type(boolean) ident(sourceBeforeTarget) operator(=) operator(()ident(draggedIndex) operator(<) ident(index)(\))
+ ident(model)operator(.)ident(remove)operator(()ident(draggedIndex)(\))
+ ident(model)operator(.)ident(add)operator(()operator(()ident(sourceBeforeTarget) operator(?) ident(index) operator(-) integer(1) operator(:) ident(index)(\))operator(,) ident(dragged)(\))
+ ident(dtde)operator(.)ident(dropComplete)operator(()keyword(true)(\))
+ (})
+(})
+
+type(class) class(CustomTransferable) directive(implements) pre_type(Transferable) operator({)
+ keyword(def) ident(object)
+ keyword(def) ident(ddlist)
+
+ directive(public) ident(CustomTransferable)operator(()ident(object)operator(,) ident(ddlist)(\)) operator({)
+ local_variable(this)operator(.)ident(object) operator(=) ident(object)
+ local_variable(this)operator(.)ident(ddlist) operator(=) ident(ddlist)
+ (})
+
+ directive(public) pre_type(Object) ident(getTransferData)operator(()pre_type(DataFlavor) ident(df)(\)) operator({)
+ keyword(if) operator(()ident(isDataFlavorSupported)operator(()ident(df)(\)\)) keyword(return) ident(object)
+ (})
+
+ directive(public) type(boolean) ident(isDataFlavorSupported)operator(()pre_type(DataFlavor) ident(df)(\)) operator({)
+ keyword(return) ident(df)operator(.)ident(equals)operator(()ident(ddlist)operator(.)ident(localDataFlavor)(\))
+ (})
+
+ directive(public) pre_type(DataFlavor)type([]) ident(getTransferDataFlavors)operator(()(\)) operator({)
+ keyword(return) ident(ddlist)operator(.)ident(supportedFlavors)
+ (})
+(})
+
+type(class) class(DragDropCellRenderer) directive(extends) pre_type(DefaultListCellRenderer) operator({)
+ type(boolean) ident(isTargetCell)
+ keyword(def) ident(ddlist)
+
+ directive(public) ident(DragDropCellRenderer)operator(()ident(ddlist)(\)) operator({)
+ local_variable(super)operator(()(\))
+ local_variable(this)operator(.)ident(ddlist) operator(=) ident(ddlist)
+ (})
+
+ directive(public) pre_type(Component) ident(getListCellRendererComponent)operator(()pre_type(JList) ident(list)operator(,) pre_type(Object) ident(value)operator(,)
+ type(int) ident(index)operator(,) type(boolean) ident(isSelected)operator(,) type(boolean) ident(hasFocus)(\)) operator({)
+ ident(isTargetCell) operator(=) operator(()ident(value) operator(==) ident(ddlist)operator(.)ident(dropTargetCell)(\))
+ type(boolean) ident(showSelected) operator(=) ident(isSelected) operator(&&) operator(!)ident(isTargetCell)
+ keyword(return) local_variable(super)operator(.)ident(getListCellRendererComponent)operator(()ident(list)operator(,) ident(value)operator(,) ident(index)operator(,) ident(showSelected)operator(,) ident(hasFocus)(\))
+ (})
+
+ directive(public) type(void) ident(paintComponent)operator(()pre_type(Graphics) ident(g)(\)) operator({)
+ local_variable(super)operator(.)ident(paintComponent)operator(()ident(g)(\))
+ keyword(if) operator(()ident(isTargetCell)(\)) operator({)
+ ident(g)operator(.)ident(setColor)operator(()pre_type(Color)operator(.)ident(black)(\))
+ ident(g)operator(.)ident(drawLine)operator(()integer(0)operator(,) integer(0)operator(,) ident(size)operator(.)ident(width)operator(.)ident(intValue)operator(()(\))operator(,) integer(0)(\))
+ (})
+ (})
+(})
+
+ident(lines) operator(=) string<delimiter(''')content(
+This is line 1
+This is line 2
+This is line 3
+This is line 4
+)delimiter(''')>operator(.)ident(trim)operator(()(\))operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+keyword(def) ident(listModel) operator(=) keyword(new) pre_type(DefaultListModel)operator(()(\))
+ident(lines)operator(.)ident(each)operator({) ident(listModel)operator(.)ident(addElement)operator(()local_variable(it)(\)) (})
+ident(listModel)operator(.)ident(addElement)operator(()string<delimiter(')content( )delimiter(')>(\)) comment(// dummy)
+keyword(def) ident(list) operator(=) keyword(new) ident(DragDropList)operator(()ident(listModel)(\))
+keyword(def) ident(sp) operator(=) keyword(new) pre_type(JScrollPane)operator(()ident(list)operator(,) ident(SPC)operator(.)ident(VERTICAL_SCROLLBAR_ALWAYS)operator(,) ident(SPC)operator(.)ident(HORIZONTAL_SCROLLBAR_NEVER)(\))
+keyword(def) ident(frame) operator(=) keyword(new) pre_type(JFrame)operator(()string<delimiter(')content(Line Shuffle Example)delimiter(')>(\))
+ident(frame)operator(.)ident(setDefaultCloseOperation)operator(()pre_type(WindowConstants)operator(.)ident(EXIT_ON_CLOSE)(\))
+ident(frame)operator(.)ident(contentPane)operator(.)ident(add)operator(()ident(sp)(\))
+ident(frame)operator(.)ident(pack)operator(()(\))
+ident(frame)operator(.)ident(setVisible)operator(()keyword(true)(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.1)
+comment(//----------------------------------------------------------------------------------)
+ident(output) operator(=) string<delimiter(")content(program args)delimiter(")>operator(.)ident(execute)operator(()(\))operator(.)ident(text)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.2)
+comment(//----------------------------------------------------------------------------------)
+ident(proc) operator(=) string<delimiter(")content(vi myfile)delimiter(")>operator(.)ident(execute)operator(()(\))
+ident(proc)operator(.)ident(waitFor)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// Calling execute(\) on a String, String[] or List (of Strings or objects with)
+comment(// a toString(\) method\) will fork off another process.)
+comment(// This doesn't replace the existing process but if you simply finish the original)
+comment(// process (leaving the spawned process to finish asynchronously\) you will achieve)
+comment(// a similar thing.)
+string<delimiter(")content(archive *.data)delimiter(")>operator(.)ident(execute)operator(()(\))
+operator([)string<delimiter(")content(archive)delimiter(")>operator(,) string<delimiter(")content(accounting.data)delimiter(")>(])operator(.)ident(execute)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// sending text to the input of another process)
+ident(proc) operator(=) string<delimiter(')content(groovy -e "print System.in.text.toUpperCase(\)")delimiter(')>operator(.)ident(execute)operator(()(\))
+pre_type(Thread)operator(.)ident(start)operator({)
+ keyword(def) ident(writer) operator(=) keyword(new) pre_type(PrintWriter)operator(()keyword(new) pre_type(BufferedOutputStream)operator(()ident(proc)operator(.)ident(out)(\)\))
+ ident(writer)operator(.)ident(println)operator(()string<delimiter(')content(Hello)delimiter(')>(\))
+ ident(writer)operator(.)ident(close)operator(()(\))
+(})
+ident(proc)operator(.)ident(waitFor)operator(()(\))
+comment(// further process output from process)
+ident(print) ident(proc)operator(.)ident(text)operator(.)ident(reverse)operator(()(\))
+comment(// =>)
+comment(// OLLEH)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// filter your own output)
+ident(keep) operator(=) pre_type(System)operator(.)ident(out)
+ident(pipe) operator(=) keyword(new) pre_type(PipedInputStream)operator(()(\))
+ident(reader) operator(=) keyword(new) pre_type(BufferedReader)operator(()keyword(new) pre_type(InputStreamReader)operator(()ident(pipe)(\)\))
+pre_type(System)operator(.)ident(setOut)operator(()keyword(new) pre_type(PrintStream)operator(()keyword(new) pre_type(BufferedOutputStream)operator(()keyword(new) pre_type(PipedOutputStream)operator(()ident(pipe)(\)\)\)\))
+type(int) ident(numlines) operator(=) integer(2)
+pre_type(Thread)operator(.)ident(start)operator({)
+ keyword(while)operator(()operator(()ident(next) operator(=) ident(reader)operator(.)ident(readLine)operator(()(\)\)) operator(!=) keyword(null)(\)) operator({)
+ keyword(if) operator(()ident(numlines)operator(--) operator(>) integer(0)(\)) ident(keep)operator(.)ident(println)operator(()ident(next)(\))
+ (})
+(})
+operator(()integer(1)operator(..)integer(8)(\))operator(.)ident(each)operator({) ident(println) local_variable(it) (})
+pre_type(System)operator(.)ident(out)operator(.)ident(close)operator(()(\))
+pre_type(System)operator(.)ident(setOut)operator(()ident(keep)(\))
+operator(()integer(9)operator(..)integer(10)(\))operator(.)ident(each)operator({) ident(println) local_variable(it) (})
+comment(// =>)
+comment(// 1)
+comment(// 2)
+comment(// 9)
+comment(// 10)
+
+
+comment(// filtering output by adding quotes and numbers)
+type(class) class(FilterOutput) directive(extends) pre_type(Thread) operator({)
+ ident(Closure) ident(c)
+ pre_type(Reader) ident(reader)
+ pre_type(PrintStream) ident(orig)
+ ident(FilterOutput)operator(()ident(Closure) ident(c)(\)) operator({)
+ local_variable(this)operator(.)ident(c) operator(=) ident(c)
+ ident(orig) operator(=) pre_type(System)operator(.)ident(out)
+ keyword(def) ident(pipe) operator(=) keyword(new) pre_type(PipedInputStream)operator(()(\))
+ ident(reader) operator(=) keyword(new) pre_type(BufferedReader)operator(()keyword(new) pre_type(InputStreamReader)operator(()ident(pipe)(\)\))
+ pre_type(System)operator(.)ident(setOut)operator(()keyword(new) pre_type(PrintStream)operator(()keyword(new) pre_type(BufferedOutputStream)operator(()keyword(new) pre_type(PipedOutputStream)operator(()ident(pipe)(\)\)\)\))
+ (})
+ type(void) ident(run)operator(()(\)) operator({)
+ keyword(def) ident(next)
+ keyword(while)operator(()operator(()ident(next) operator(=) ident(reader)operator(.)ident(readLine)operator(()(\)\)) operator(!=) keyword(null)(\)) operator({)
+ ident(c)operator(()ident(orig)operator(,) ident(next)(\))
+ (})
+ (})
+ keyword(def) method(close)operator(()(\)) operator({)
+ ident(sleep) integer(100)
+ pre_type(System)operator(.)ident(out)operator(.)ident(close)operator(()(\))
+ pre_type(System)operator(.)ident(setOut)operator(()ident(orig)(\))
+ (})
+(})
+ident(cnt) operator(=) integer(0)
+ident(number) operator(=) operator({) ident(s)operator(,) ident(n) operator(->) ident(cnt)operator(++)operator(;) ident(s)operator(.)ident(println)operator(()ident(cnt) operator(+) string<delimiter(')content(:)delimiter(')> operator(+) ident(n)(\)) (})
+ident(quote) operator(=) operator({) ident(s)operator(,) ident(n) operator(->) ident(s)operator(.)ident(println)operator(()string<delimiter(')content(> )delimiter(')> operator(+) ident(n)(\)) (})
+ident(f1) operator(=) keyword(new) ident(FilterOutput)operator(()ident(number)(\))operator(;) ident(f1)operator(.)ident(start)operator(()(\))
+ident(f2) operator(=) keyword(new) ident(FilterOutput)operator(()ident(quote)(\))operator(;) ident(f2)operator(.)ident(start)operator(()(\))
+operator(()string<delimiter(')content(a)delimiter(')>operator(..)string<delimiter(')content(e)delimiter(')>(\))operator(.)ident(each)operator({) ident(println) local_variable(it) (})
+ident(f2)operator(.)ident(close)operator(()(\))
+ident(f1)operator(.)ident(close)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy programs (like Java ones\) would use streams here. Just process)
+comment(// another stream instead of System.in or System.out:)
+
+comment(// process url text)
+ident(input) operator(=) keyword(new) pre_type(URL)operator(()ident(address)(\))operator(.)ident(openStream)operator(()(\))
+comment(// ... process 'input' stream)
+
+comment(// process compressed file)
+ident(input) operator(=) keyword(new) pre_type(GZIPInputStream)operator(()keyword(new) pre_type(FileInputStream)operator(()string<delimiter(')content(source.gzip)delimiter(')>(\)\))
+comment(// ... process 'input' stream)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.7)
+comment(//----------------------------------------------------------------------------------)
+comment(// To read STDERR of a process you execute)
+ident(proc) operator(=) string<delimiter(')content(groovy -e "println args[0]")delimiter(')>operator(.)ident(execute)operator(()(\))
+ident(proc)operator(.)ident(waitFor)operator(()(\))
+ident(println) ident(proc)operator(.)ident(err)operator(.)ident(text)
+comment(// => Caught: java.lang.ArrayIndexOutOfBoundsException: 0 ...)
+
+comment(// To redirect your STDERR to a file)
+pre_type(System)operator(.)ident(setErr)operator(()keyword(new) pre_type(PrintStream)operator(()keyword(new) pre_type(FileOutputStream)operator(()string<delimiter(")content(error.txt)delimiter(")>(\)\)\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.8)
+comment(//----------------------------------------------------------------------------------)
+comment(// See 16.2, the technique allows both STDIN and STDOUT of another program to be)
+comment(// changed at the same time, not just one or the other as per Perl 16.2 solution)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.9)
+comment(//----------------------------------------------------------------------------------)
+comment(// See 16.2 and 16.7, the techniques can be combined to allow all three streams)
+comment(// (STDIN, STDOUT, STDERR\) to be altered as required.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.10)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy can piggy-back on the many options available to Java here:)
+comment(// JIPC provides a wide set of standard primitives: semaphore, event,)
+comment(// FIFO queue, barrier, shared memory, shared and exclusive locks:)
+comment(// http://www.garret.ru/~knizhnik/jipc/jipc.html)
+comment(// sockets allow process to communicate via low-level packets)
+comment(// CORBA, RMI, SOAP allow process to communicate via RPC calls)
+comment(// shared files can also be used)
+comment(// JMS allows process to communicate via a messaging service)
+
+comment(// Simplist approach is to just link streams:)
+ident(proc1) operator(=) string<delimiter(')content(groovy -e "println args[0]" Hello)delimiter(')>operator(.)ident(execute)operator(()(\))
+ident(proc2) operator(=) string<delimiter(')content(groovy -e "print System.in.text.toUpperCase(\)")delimiter(')>operator(.)ident(execute)operator(()(\))
+pre_type(Thread)operator(.)ident(start)operator({)
+ keyword(def) ident(reader) operator(=) keyword(new) pre_type(BufferedReader)operator(()keyword(new) pre_type(InputStreamReader)operator(()ident(proc1)operator(.)ident(in)(\)\))
+ keyword(def) ident(writer) operator(=) keyword(new) pre_type(PrintWriter)operator(()keyword(new) pre_type(BufferedOutputStream)operator(()ident(proc2)operator(.)ident(out)(\)\))
+ keyword(while) operator(()operator(()ident(next) operator(=) ident(reader)operator(.)ident(readLine)operator(()(\)\)) operator(!=) keyword(null)(\)) operator({)
+ ident(writer)operator(.)ident(println)operator(()ident(next)(\))
+ (})
+ ident(writer)operator(.)ident(close)operator(()(\))
+(})
+ident(proc2)operator(.)ident(waitFor)operator(()(\))
+ident(print) ident(proc2)operator(.)ident(text)
+comment(// => HELLO)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// Java/Groovy would normally just use some socket-based technique for communicating)
+comment(// between processes (see 16.10 for a list of options\). If you really must use a named)
+comment(// pipe, you have these options:)
+comment(// (1\) On *nix machines:)
+comment(// * Create a named pipe by invoking the mkfifo utility using execute(\).)
+comment(// * Open a named pipe by name - which is just like opening a file.)
+comment(// * Run an external process setting its input and output streams (see 16.1, 16.4, 16.5\))
+comment(// (2\) On Windows machines, Using JCIFS to Connect to Win32 Named Pipes, see:)
+comment(// http://jcifs.samba.org/src/docs/pipes.html)
+comment(// Neither of these achieve exactly the same result as the Perl example but some)
+comment(// scenarios will be almost identical.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.12)
+comment(//----------------------------------------------------------------------------------)
+comment(// The comments made in 16.10 regarding other alternative IPC mechanisms also apply here.)
+
+comment(// This example would normally be done with multiple threads in Java/Groovy as follows.)
+type(class) class(Shared) operator({)
+ pre_type(String) ident(buffer) operator(=) string<delimiter(")content(not set yet)delimiter(")>
+ directive(synchronized) type(void) ident(leftShift)operator(()ident(value)(\))operator({)
+ ident(buffer) operator(=) ident(value)
+ ident(notifyAll)operator(()(\))
+ (})
+ directive(synchronized) pre_type(Object) ident(read)operator(()(\)) operator({)
+ keyword(return) ident(buffer)
+ (})
+(})
+ident(shared) operator(=) keyword(new) ident(Shared)operator(()(\))
+ident(rand) operator(=) keyword(new) pre_type(Random)operator(()(\))
+ident(threads) operator(=) type([])
+operator(()integer(1)operator(..)integer(5)(\))operator(.)ident(each)operator({)
+ ident(t) operator(=) keyword(new) pre_type(Thread)operator(()(\))operator({)
+ keyword(def) ident(me) operator(=) ident(t)
+ keyword(for) operator(()ident(j) keyword(in) integer(0)operator(..)integer(9)(\)) operator({)
+ ident(shared) operator(<)operator(<) string<delimiter(")inline<inline_delimiter($)ident(me)>content(.name )inline<inline_delimiter($)ident(j)>delimiter(")>
+ ident(sleep) integer(100) operator(+) ident(rand)operator(.)ident(nextInt)operator(()integer(200)(\))
+ (})
+ (})
+ ident(t)operator(.)ident(start)operator(()(\))
+(})
+keyword(while)operator(()integer(1)(\)) operator({)
+ ident(println) ident(shared)operator(.)ident(read)operator(()(\))
+ ident(sleep) integer(50)
+(})
+comment(// =>)
+comment(// not set yet)
+comment(// Thread-2 0)
+comment(// Thread-5 1)
+comment(// Thread-1 1)
+comment(// Thread-4 2)
+comment(// Thread-3 1)
+comment(// ...)
+comment(// Thread-5 9)
+
+
+comment(// Using JIPC between processes (as a less Groovy alternative that is closer)
+comment(// to the original cookbook\) is shown below.)
+
+comment(// ipcWriterScript:)
+keyword(import) include(org.garret.jipc.client.JIPCClientFactory)
+ident(port) operator(=) integer(6000)
+ident(factory) operator(=) ident(JIPCClientFactory)operator(.)ident(instance)
+ident(session) operator(=) ident(factory)operator(.)ident(create)operator(()string<delimiter(')content(localhost)delimiter(')>operator(,) ident(port)(\))
+ident(mutex) operator(=) ident(session)operator(.)ident(createMutex)operator(()string<delimiter(")content(myMutex)delimiter(")>operator(,) keyword(false)(\))
+ident(buffer) operator(=) ident(session)operator(.)ident(createSharedMemory)operator(()string<delimiter(")content(myBuffer)delimiter(")>operator(,) string<delimiter(")content(not yet set)delimiter(")>(\))
+ident(name) operator(=) ident(args)operator([)integer(0)(])
+ident(rand) operator(=) keyword(new) pre_type(Random)operator(()(\))
+operator(()integer(0)operator(..)integer(99)(\))operator(.)ident(each) operator({)
+ ident(mutex)operator(.)ident(lock)operator(()(\))
+ ident(buffer)operator(.)ident(set)operator(()string<delimiter(")inline<inline_delimiter($)ident(name)>content( )inline<inline_delimiter($)local_variable(it)>delimiter(")>operator(.)ident(toString)operator(()(\)\))
+ ident(mutex)operator(.)ident(unlock)operator(()(\))
+ ident(sleep) integer(200) operator(+) ident(rand)operator(.)ident(nextInt)operator(()integer(500)(\))
+(})
+ident(session)operator(.)ident(close)operator(()(\))
+
+comment(// ipcReaderScript:)
+keyword(import) include(org.garret.jipc.client.JIPCClientFactory)
+ident(port) operator(=) integer(6000)
+ident(factory) operator(=) ident(JIPCClientFactory)operator(.)ident(instance)
+ident(session) operator(=) ident(factory)operator(.)ident(create)operator(()string<delimiter(')content(localhost)delimiter(')>operator(,) ident(port)(\))
+ident(mutex) operator(=) ident(session)operator(.)ident(createMutex)operator(()string<delimiter(")content(myMutex)delimiter(")>operator(,) keyword(false)(\))
+ident(buffer) operator(=) ident(session)operator(.)ident(createSharedMemory)operator(()string<delimiter(")content(myBuffer)delimiter(")>operator(,) string<delimiter(")content(not yet set)delimiter(")>(\))
+ident(rand) operator(=) keyword(new) pre_type(Random)operator(()(\))
+operator(()integer(0)operator(..)integer(299)(\))operator(.)ident(each) operator({)
+ ident(mutex)operator(.)ident(lock)operator(()(\))
+ ident(println) ident(buffer)operator(.)ident(get)operator(()(\))
+ ident(mutex)operator(.)ident(unlock)operator(()(\))
+ ident(sleep) integer(150)
+(})
+ident(session)operator(.)ident(close)operator(()(\))
+
+comment(// kick off processes:)
+string<delimiter(")content(java org.garret.jipc.server.JIPCServer 6000)delimiter(")>operator(.)ident(execute)operator(()(\))
+string<delimiter(")content(groovy ipcReaderScript)delimiter(")>operator(.)ident(execute)operator(()(\))
+operator(()integer(0)operator(..)integer(3)(\))operator(.)ident(each)operator({) string<delimiter(")content(groovy ipcWriterScript )inline<inline_delimiter($)local_variable(it)>delimiter(")>operator(.)ident(execute)operator(()(\)) (})
+
+comment(// =>)
+comment(// ...)
+comment(// 0 10)
+comment(// 2 10)
+comment(// 2 11)
+comment(// 1 9)
+comment(// 1 9)
+comment(// 1 10)
+comment(// 2 12)
+comment(// 3 12)
+comment(// 3 12)
+comment(// 2 13)
+comment(// ...)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// Signal handling in Groovy (like Java\) is operating system and JVM dependent.)
+comment(// The ISO C standard only requires the signal names SIGABRT, SIGFPE, SIGILL,)
+comment(// SIGINT, SIGSEGV, and SIGTERM to be defined but depending on your platform)
+comment(// other signals may be present, e.g. Windows supports SIGBREAK. For more info)
+comment(// see: http://www-128.ibm.com/developerworks/java/library/i-signalhandling/)
+comment(// Note: if you start up the JVM with -Xrs the JVM will try to reduce its)
+comment(// internal usage of signals. Also the JVM takes over meany hooks and provides)
+comment(// platform independent alternatives, e.g. see java.lang.Runtime#addShutdownHook(\))
+
+comment(// To see what signals are available for your system (excludes ones taken over)
+comment(// by the JVM\):)
+ident(sigs) operator(=) string<delimiter(''')content(HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM
+USR1 USR2 CHLD PWR WINCH URG POLL STOP TSTP CONT TTIN TTOU VTALRM PROF XCPU
+XFSZ WAITING LWP AIO IO INFO THR BREAK FREEZE THAW CANCEL EMT
+)delimiter(''')>
+
+ident(sigs)operator(.)ident(tokenize)operator(()string<delimiter(')content( )content(\\n)delimiter(')>(\))operator(.)ident(each)operator({)
+ keyword(try) operator({)
+ ident(print) string<delimiter(')content( )delimiter(')> operator(+) keyword(new) ident(sun)operator(.)ident(misc)operator(.)ident(Signal)operator(()local_variable(it)(\))
+ (}) keyword(catch)operator(()pre_type(IllegalArgumentException) ident(iae)(\)) operator({)(})
+(})
+comment(// => on Windows XP:)
+comment(// SIGINT SIGILL SIGABRT SIGFPE SIGSEGV SIGTERM SIGBREAK)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.14)
+comment(//----------------------------------------------------------------------------------)
+comment(// To send a signal to your process:)
+ident(Signal)operator(.)ident(raise)operator(()keyword(new) ident(Signal)operator(()string<delimiter(")content(INT)delimiter(")>(\)\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.15)
+comment(//----------------------------------------------------------------------------------)
+comment(// install a signal handler)
+type(class) class(DiagSignalHandler) directive(implements) ident(SignalHandler) operator({) operator(..)operator(.) (})
+ident(diagHandler) operator(=) keyword(new) ident(DiagSignalHandler)operator(()(\))
+ident(Signal)operator(.)ident(handle)operator(()keyword(new) ident(Signal)operator(()string<delimiter(")content(INT)delimiter(")>(\))operator(,) ident(diagHandler)(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.16)
+comment(//----------------------------------------------------------------------------------)
+comment(// temporarily install a signal handler)
+type(class) class(DiagSignalHandler) directive(implements) ident(SignalHandler) operator({) operator(..)operator(.) (})
+ident(diagHandler) operator(=) keyword(new) ident(DiagSignalHandler)operator(()(\))
+ident(oldHandler) operator(=) ident(Signal)operator(.)ident(handle)operator(()keyword(new) ident(Signal)operator(()string<delimiter(")content(INT)delimiter(")>(\))operator(,) ident(diagHandler)(\))
+ident(Signal)operator(.)ident(handle)operator(()keyword(new) ident(Signal)operator(()string<delimiter(")content(INT)delimiter(")>(\))operator(,) ident(oldHandler)(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.17)
+comment(//----------------------------------------------------------------------------------)
+keyword(import) include(sun.misc.Signal)
+keyword(import) include(sun.misc.SignalHandler)
+
+type(class) class(DiagSignalHandler) directive(implements) ident(SignalHandler) operator({)
+ directive(private) ident(oldHandler)
+
+ comment(// Static method to install the signal handler)
+ directive(public) directive(static) ident(install)operator(()ident(signal)(\)) operator({)
+ keyword(def) ident(diagHandler) operator(=) keyword(new) ident(DiagSignalHandler)operator(()(\))
+ ident(diagHandler)operator(.)ident(oldHandler) operator(=) ident(Signal)operator(.)ident(handle)operator(()ident(signal)operator(,) ident(diagHandler)(\))
+ (})
+
+ directive(public) type(void) ident(handle)operator(()ident(Signal) ident(sig)(\)) operator({)
+ ident(println)operator(()string<delimiter(")content(Diagnostic Signal handler called for signal )delimiter(")>operator(+)ident(sig)(\))
+ comment(// Output information for each thread)
+ keyword(def) ident(list) operator(=) type([])
+ pre_type(Thread)operator(.)ident(activeCount)operator(()(\))operator(.)ident(each)operator({) ident(list) operator(+=) keyword(null) (})
+ pre_type(Thread)type([]) ident(threadArray) operator(=) ident(list) keyword(as) pre_type(Thread)type([])
+ type(int) ident(numThreads) operator(=) pre_type(Thread)operator(.)ident(enumerate)operator(()ident(threadArray)(\))
+ ident(println)operator(()string<delimiter(")content(Current threads:)delimiter(")>(\))
+ keyword(for) operator(()ident(i) keyword(in) integer(0)operator(..<)ident(numThreads)(\)) operator({)
+ ident(println)operator(()string<delimiter(")content( )delimiter(")>operator(+)ident(threadArray)operator([)ident(i)(]\))
+ (})
+
+ comment(// Chain back to previous handler, if one exists)
+ keyword(if) operator(() ident(oldHandler) operator(!=) ident(SIG_DFL) operator(&&) ident(oldHandler) operator(!=) ident(SIG_IGN) (\)) operator({)
+ ident(oldHandler)operator(.)ident(handle)operator(()ident(sig)(\))
+ (})
+ (})
+(})
+comment(// install using:)
+ident(DiagSignalHandler)operator(.)ident(install)operator(()keyword(new) ident(Signal)operator(()string<delimiter(")content(INT)delimiter(")>(\)\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.18)
+comment(//----------------------------------------------------------------------------------)
+comment(// See 16.17, just don't chain to the previous handler because the default handler)
+comment(// will abort the process.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.19)
+comment(//----------------------------------------------------------------------------------)
+comment(// Groovy relies on Java features here. Java doesn't keep the process around)
+comment(// as it stores metadata in a Process object. You can call waitFor(\) or destroy(\))
+comment(// or exitValue(\) on the Process object. If the Process object is garbage collected,)
+comment(// the process can still execute asynchronously with respect to the original process.)
+
+comment(// For ensuring processes don't die, see:)
+comment(// http://jakarta.apache.org/commons/daemon/)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.20)
+comment(//----------------------------------------------------------------------------------)
+comment(// There is no equivalent to a signal mask available directly in Groovy or Java.)
+comment(// You can override and ignore individual signals using recipes 16.16 - 16.18.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.21)
+comment(//----------------------------------------------------------------------------------)
+ident(t) operator(=) keyword(new) pre_type(Timer)operator(()(\))
+ident(t)operator(.)ident(runAfter)operator(()integer(3500)(\))operator({)
+ ident(println) string<delimiter(')content(Took too long)delimiter(')>
+ pre_type(System)operator(.)ident(exit)operator(()integer(1)(\))
+(})
+keyword(def) ident(count) operator(=) integer(0)
+integer(6)operator(.)ident(times)operator({)
+ ident(count)operator(++)
+ ident(sleep) integer(1000)
+ ident(println) string<delimiter(")content(Count = )inline<inline_delimiter($)ident(count)>delimiter(")>
+(})
+ident(t)operator(.)ident(cancel)operator(()(\))
+comment(// See also special JMX timer class: javax.management.timer.Timer)
+comment(// For an external process you can also use: proc.waitForOrKill(3500\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_16.22)
+comment(//----------------------------------------------------------------------------------)
+comment(// One way to implement this functionality is to automatically replace the ~/.plan)
+comment(// etc. files every fixed timed interval - though this wouldn't be efficient.)
+comment(// Here is a simplified version which is a simplified version compared to the)
+comment(// original cookbook. It only looks at the ~/.signature file and changes it)
+comment(// freely. It also doesn't consider other news reader related files.)
+
+keyword(def) ident(sigs) operator(=) string<delimiter(''')content(
+Make is like Pascal: everybody likes it, so they go in and change it.
+--Dennis Ritchie
+%%
+I eschew embedded capital letters in names; to my prose-oriented eyes,
+they are too awkward to read comfortably. They jangle like bad typography.
+--Rob Pike
+%%
+God made the integers; all else is the work of Man.
+--Kronecker
+%%
+I d rather have :rofix than const. --Dennis Ritchie
+%%
+If you want to program in C, program in C. It s a nice language.
+I use it occasionally... :-\) --Larry Wall
+%%
+Twisted cleverness is my only skill as a programmer.
+--Elizabeth Zwicky
+%%
+Basically, avoid comments. If your code needs a comment to be understood,
+it would be better to rewrite it so it s easier to understand.
+--Rob Pike
+%%
+Comments on data are usually much more helpful than on algorithms.
+--Rob Pike
+%%
+Programs that write programs are the happiest programs in the world.
+--Andrew Hume
+)delimiter(''')>operator(.)ident(trim)operator(()(\))operator(.)ident(split)operator(()regexp<delimiter(/)char(\\n)content(%%)char(\\n)delimiter(/)>(\))
+ident(name) operator(=) string<delimiter(')content(me@somewhere.org)content(\\n)delimiter(')>
+ident(file) operator(=) keyword(new) pre_type(File)operator(()pre_type(System)operator(.)ident(getProperty)operator(()string<delimiter(')content(user.home)delimiter(')>(\)) operator(+) pre_type(File)operator(.)ident(separator) operator(+) string<delimiter(')content(.signature)delimiter(')>(\))
+ident(rand) operator(=) keyword(new) pre_type(Random)operator(()(\))
+keyword(while)operator(()integer(1)(\)) operator({)
+ ident(file)operator(.)ident(delete)operator(()(\))
+ ident(file) operator(<)operator(<) ident(name) operator(+) ident(sigs)operator([)ident(rand)operator(.)ident(nextInt)operator(()ident(sigs)operator(.)ident(size)operator(()(\)\)])
+ ident(sleep) integer(10000)
+(})
+
+comment(// Another way to implement this functionality (in a completely different way to the)
+comment(// original cookbook\) is to use a FileWatcher class, e.g.)
+comment(// http://www.rgagnon.com/javadetails/java-0490.html (FileWatcher and DirWatcher\))
+comment(// http://www.jconfig.org/javadoc/org/jconfig/FileWatcher.html)
+
+comment(// These file watchers notify us whenever the file is modified, see Pleac chapter 7)
+comment(// for workarounds to not being able to get last accessed time vs last modified time.)
+comment(// (We would now need to touch the file whenever we accessed it to make it change\).)
+comment(// Our handler called from the watchdog class would update the file contents.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.0)
+comment(//----------------------------------------------------------------------------------)
+ident(myClient) operator(=) keyword(new) pre_type(Socket)operator(()string<delimiter(")content(Machine name)delimiter(")>operator(,) ident(portNumber)(\))
+ident(myAddress) operator(=) ident(myClient)operator(.)ident(inetAddress)
+ident(myAddress)operator(.)ident(hostAddress) comment(// string representation of host address)
+ident(myAddress)operator(.)ident(hostName) comment(// host name)
+ident(myAddress)operator(.)ident(address) comment(// IP address as array of bytes)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.1)
+comment(//----------------------------------------------------------------------------------)
+ident(s) operator(=) keyword(new) pre_type(Socket)operator(()string<delimiter(")content(localhost)delimiter(")>operator(,) integer(5000)(\))operator(;)
+ident(s) operator(<)operator(<) string<delimiter(")content(Why don't you call me anymore?)char(\\n)delimiter(")>
+ident(s)operator(.)ident(close)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// commandline socket server echoing input back to originator)
+ident(groovy) operator(-)ident(l) integer(5000) operator(-)ident(e) string<delimiter(")content(println line)delimiter(")>
+
+comment(// commandline socket server eching input to stderr)
+ident(groovy) operator(-)ident(l) integer(5000) operator(-)ident(e) string<delimiter(")content(System.err.println line)delimiter(")>
+
+comment(// a web server as a script (extension to cookbook\))
+ ident(server) operator(=) keyword(new) pre_type(ServerSocket)operator(()integer(5000)(\))
+ keyword(while)operator(()keyword(true)(\)) operator({)
+ ident(server)operator(.)ident(accept)operator(()(\)) operator({) ident(socket) operator(->)
+ ident(socket)operator(.)ident(withStreams) operator({) ident(input)operator(,) ident(output) operator(->)
+ comment(// ignore input and just serve dummy content)
+ ident(output)operator(.)ident(withWriter) operator({) ident(writer) operator(->)
+ ident(writer) operator(<)operator(<) string<delimiter(")content(HTTP/1.1 200 OK)char(\\n)delimiter(")>
+ ident(writer) operator(<)operator(<) string<delimiter(")content(Content-Type: text/html)char(\\n)char(\\n)delimiter(")>
+ ident(writer) operator(<)operator(<) string<delimiter(")content(<html><body>Hello World! It's )inline<inline_delimiter(${)ident(new Date(\))inline_delimiter(})>content(</body></html>)char(\\n)delimiter(")>
+ (})
+ (})
+ (})
+ (})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.3)
+comment(//----------------------------------------------------------------------------------)
+ident(server) operator(=) keyword(new) pre_type(ServerSocket)operator(()integer(5000)(\))
+keyword(while)operator(()keyword(true)(\)) operator({)
+ ident(server)operator(.)ident(accept)operator(()(\)) operator({) ident(socket) operator(->)
+ ident(socket)operator(.)ident(withStreams) operator({) ident(input)operator(,) ident(output) operator(->)
+ ident(w) operator(=) keyword(new) pre_type(PrintWriter)operator(()ident(output)(\))
+ ident(w) operator(<)operator(<) string<delimiter(")content(What is your name? )delimiter(")>
+ ident(w)operator(.)ident(flush)operator(()(\))
+ ident(r) operator(=) ident(input)operator(.)ident(readLine)operator(()(\))
+ pre_type(System)operator(.)ident(err)operator(.)ident(println) string<delimiter(")content(User responded with )inline<inline_delimiter($)ident(r)>delimiter(")>
+ ident(w)operator(.)ident(close)operator(()(\))
+ (})
+ (})
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// UDP client)
+ident(data) operator(=) string<delimiter(")content(Message)delimiter(")>operator(.)ident(getBytes)operator(()string<delimiter(")content(ASCII)delimiter(")>(\))
+ident(addr) operator(=) pre_type(InetAddress)operator(.)ident(getByName)operator(()string<delimiter(")content(localhost)delimiter(")>(\))
+ident(port) operator(=) integer(5000)
+ident(packet) operator(=) keyword(new) pre_type(DatagramPacket)operator(()ident(data)operator(,) ident(data)operator(.)ident(length)operator(,) ident(addr)operator(,) ident(port)(\))
+ident(socket) operator(=) keyword(new) pre_type(DatagramSocket)operator(()(\))
+ident(socket)operator(.)ident(send)operator(()ident(packet)(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// UDP server)
+ident(socket) operator(=) keyword(new) pre_type(DatagramSocket)operator(()integer(5000)(\))
+ident(buffer) operator(=) operator(()string<delimiter(')content( )delimiter(')> operator(*) integer(4096)(\)) keyword(as) type(byte)type([])
+keyword(while)operator(()keyword(true)(\)) operator({)
+ ident(incoming) operator(=) keyword(new) pre_type(DatagramPacket)operator(()ident(buffer)operator(,) ident(buffer)operator(.)ident(length)(\))
+ ident(socket)operator(.)ident(receive)operator(()ident(incoming)(\))
+ ident(s) operator(=) keyword(new) pre_type(String)operator(()ident(incoming)operator(.)ident(data)operator(,) integer(0)operator(,) ident(incoming)operator(.)ident(length)(\))
+ pre_type(String) ident(reply) operator(=) string<delimiter(")content(Client said: ')inline<inline_delimiter($)ident(s)>content(')delimiter(")>
+ ident(outgoing) operator(=) keyword(new) pre_type(DatagramPacket)operator(()ident(reply)operator(.)ident(bytes)operator(,) ident(reply)operator(.)ident(size)operator(()(\))operator(,)
+ ident(incoming)operator(.)ident(address)operator(,) ident(incoming)operator(.)ident(port)(\))operator(;)
+ ident(socket)operator(.)ident(send)operator(()ident(outgoing)(\))
+(})
+
+comment(// UDP client)
+ident(data) operator(=) string<delimiter(")content(Original Message)delimiter(")>operator(.)ident(getBytes)operator(()string<delimiter(")content(ASCII)delimiter(")>(\))
+ident(addr) operator(=) pre_type(InetAddress)operator(.)ident(getByName)operator(()string<delimiter(")content(localhost)delimiter(")>(\))
+ident(port) operator(=) integer(5000)
+ident(packet) operator(=) keyword(new) pre_type(DatagramPacket)operator(()ident(data)operator(,) ident(data)operator(.)ident(length)operator(,) ident(addr)operator(,) ident(port)(\))
+ident(socket) operator(=) keyword(new) pre_type(DatagramSocket)operator(()(\))
+ident(socket)operator(.)ident(send)operator(()ident(packet)(\))
+ident(socket)operator(.)ident(setSoTimeout)operator(()integer(30000)(\)) comment(// block for no more than 30 seconds)
+ident(buffer) operator(=) operator(()string<delimiter(')content( )delimiter(')> operator(*) integer(4096)(\)) keyword(as) type(byte)type([])
+ident(response) operator(=) keyword(new) pre_type(DatagramPacket)operator(()ident(buffer)operator(,) ident(buffer)operator(.)ident(length)(\))
+ident(socket)operator(.)ident(receive)operator(()ident(response)(\))
+ident(s) operator(=) keyword(new) pre_type(String)operator(()ident(response)operator(.)ident(data)operator(,) integer(0)operator(,) ident(response)operator(.)ident(length)(\))
+ident(println) string<delimiter(")content(Server said: ')inline<inline_delimiter($)ident(s)>content(')delimiter(")>
+comment(// => Server said: 'Client said: 'Original Message'')
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// DOMAIN sockets not available in cross platform form.)
+comment(// On Linux, use jbuds:)
+comment(// http://www.graphixprose.com/jbuds/)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.7)
+comment(//----------------------------------------------------------------------------------)
+comment(// TCP socket)
+ident(socketAddress) operator(=) ident(tcpSocket)operator(.)ident(remoteSocketAddress)
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(socketAddress)>content(.address, )inline<inline_delimiter($)ident(socketAddress)>content(.hostName, )inline<inline_delimiter($)ident(socketAddress)>content(.port)delimiter(")>
+comment(// UDP packet)
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(udpPacket)>content(.address, )inline<inline_delimiter($)ident(udpPacket)>content(.port)delimiter(")>
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.8)
+comment(//----------------------------------------------------------------------------------)
+comment(// Print the fully qualified domain name for this IP address)
+ident(println) pre_type(InetAddress)operator(.)ident(localHost)operator(.)ident(canonicalHostName)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.9)
+comment(//----------------------------------------------------------------------------------)
+ident(socket)operator(.)ident(shutdownInput)operator(()(\))
+ident(socket)operator(.)ident(shutdownOutput)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.10)
+comment(//----------------------------------------------------------------------------------)
+comment(// Spawn off a thread to handle each direction)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// Spawn off a thread to handle each request.)
+comment(// This is done automatically by the Groovy accept(\) method on ServerSocket.)
+comment(// See 17.3 for an example.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.12)
+comment(//----------------------------------------------------------------------------------)
+comment(// Use a thread pool)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// Consider using Selector and/or SocketChannel, ServerSocketChannel and DatagramChannel)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.14)
+comment(//----------------------------------------------------------------------------------)
+comment(// When creating a socket on a multihomed machine, use the socket constructor with)
+comment(// 4 params to select a specific address from those available:)
+ident(socket) operator(=) keyword(new) pre_type(Socket)operator(()ident(remoteAddr)operator(,) ident(remotePort)operator(,) ident(localAddr)operator(,) ident(localPort)(\))
+
+comment(// When creating a server on a multihomed machine supply the optional bindAddr param:)
+keyword(new) pre_type(ServerSocket)operator(()ident(port)operator(,) ident(queueLength)operator(,) ident(bindAddr)(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.15)
+comment(//----------------------------------------------------------------------------------)
+comment(// Fork off a thread for your server and call setDaemon(true\) on the thread.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.16)
+comment(//----------------------------------------------------------------------------------)
+comment(// Consider using special packages designed to provide robust startup/shutdown)
+comment(// capability, e.g.: http://jakarta.apache.org/commons/daemon/)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.17)
+comment(//----------------------------------------------------------------------------------)
+comment(// Alternative to cookbook as proposed inetd solution is not cross platform.)
+ident(host) operator(=) string<delimiter(')content(localhost)delimiter(')>
+keyword(for) operator(()ident(port) keyword(in) integer(1)operator(..)integer(1024)(\)) operator({)
+ keyword(try) operator({)
+ ident(s) operator(=) keyword(new) pre_type(Socket)operator(()ident(host)operator(,) ident(port)(\))
+ ident(println)operator(()string<delimiter(")content(There is a server on port )inline<inline_delimiter($)ident(port)>content( of )inline<inline_delimiter($)ident(host)>delimiter(")>(\))
+ (})
+ keyword(catch) operator(()pre_type(Exception) ident(ex)(\)) operator({)(})
+(})
+comment(// You could open a ServerSocket(\) on each unused port and monitor those.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_17.18)
+comment(//----------------------------------------------------------------------------------)
+comment(// It's not too hard to write a TCP Proxy in Groovy but numerous Java packages)
+comment(// already exist, so we might as well use one of those:)
+comment(// http://ws.apache.org/axis/java/user-guide.html#AppendixUsingTheAxisTCPMonitorTcpmon)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_18.1)
+comment(//----------------------------------------------------------------------------------)
+ident(name) operator(=) string<delimiter(')content(www.perl.com)delimiter(')>
+ident(addresses) operator(=) pre_type(InetAddress)operator(.)ident(getAllByName)operator(()ident(name)(\))
+ident(println) ident(addresses) comment(// => {www.perl.com/208.201.239.36, www.perl.com/208.201.239.37})
+comment(// or to just resolve one:)
+ident(println) pre_type(InetAddress)operator(.)ident(getByName)operator(()ident(name)(\)) comment(// => www.perl.com/208.201.239.36)
+comment(// try a different address)
+ident(name) operator(=) string<delimiter(')content(groovy.codehaus.org)delimiter(')>
+ident(addresses) operator(=) pre_type(InetAddress)operator(.)ident(getAllByName)operator(()ident(name)(\))
+ident(println) ident(addresses) comment(// => {groovy.codehaus.org/63.246.7.187})
+comment(// starting with IP address)
+ident(address) operator(=) pre_type(InetAddress)operator(.)ident(getByAddress)operator(()operator([)integer(208)operator(,) integer(201)operator(,) integer(239)operator(,) integer(36)(]) keyword(as) type(byte)type([])(\))
+ident(println) ident(address)operator(.)ident(hostName) comment(// => www.oreillynet.com)
+
+comment(// For more complex operations use dnsjava: http://www.dnsjava.org/)
+keyword(import) include(org.xbill.DNS.*)
+pre_type(System)operator(.)ident(setProperty)operator(()string<delimiter(")content(sun.net.spi.nameservice.provider.1)delimiter(")>operator(,)string<delimiter(")content(dns,dnsjava)delimiter(")>(\))
+ident(Lookup) ident(lookup) operator(=) keyword(new) ident(Lookup)operator(()string<delimiter(')content(cnn.com)delimiter(')>operator(,) pre_type(Type)operator(.)ident(ANY)(\))
+ident(records) operator(=) ident(lookup)operator(.)ident(run)operator(()(\))
+ident(println) string<delimiter(")inline<inline_delimiter(${)ident(records?.size(\))inline_delimiter(})>content( record(s\) found)delimiter(")>
+ident(records)operator(.)ident(each)operator({) ident(println) local_variable(it) (})
+comment(// =>)
+comment(// 17 record(s\) found)
+comment(// cnn.com. 55 IN A 64.236.16.20)
+comment(// cnn.com. 55 IN A 64.236.16.52)
+comment(// cnn.com. 55 IN A 64.236.16.84)
+comment(// cnn.com. 55 IN A 64.236.16.116)
+comment(// cnn.com. 55 IN A 64.236.24.12)
+comment(// cnn.com. 55 IN A 64.236.24.20)
+comment(// cnn.com. 55 IN A 64.236.24.28)
+comment(// cnn.com. 55 IN A 64.236.29.120)
+comment(// cnn.com. 324 IN NS twdns-02.ns.aol.com.)
+comment(// cnn.com. 324 IN NS twdns-03.ns.aol.com.)
+comment(// cnn.com. 324 IN NS twdns-04.ns.aol.com.)
+comment(// cnn.com. 324 IN NS twdns-01.ns.aol.com.)
+comment(// cnn.com. 3324 IN SOA twdns-01.ns.aol.com. hostmaster.tbsnames.turner.com. 2007011203 900 300 604801 900)
+comment(// cnn.com. 3324 IN MX 10 atlmail3.turner.com.)
+comment(// cnn.com. 3324 IN MX 10 atlmail5.turner.com.)
+comment(// cnn.com. 3324 IN MX 20 nycmail2.turner.com.)
+comment(// cnn.com. 3324 IN MX 30 nycmail1.turner.com.)
+
+comment(// faster reverse lookup using dnsjava)
+keyword(def) method(reverseDns)operator(()ident(hostIp)(\)) operator({)
+ ident(name) operator(=) ident(ReverseMap)operator(.)ident(fromAddress)operator(()ident(hostIp)(\))
+ ident(rec) operator(=) ident(Record)operator(.)ident(newRecord)operator(()ident(name)operator(,) pre_type(Type)operator(.)ident(PTR)operator(,) ident(DClass)operator(.)ident(IN)(\))
+ ident(query) operator(=) ident(Message)operator(.)ident(newQuery)operator(()ident(rec)(\))
+ ident(response) operator(=) keyword(new) ident(ExtendedResolver)operator(()(\))operator(.)ident(send)operator(()ident(query)(\))
+ ident(answers) operator(=) ident(response)operator(.)ident(getSectionArray)operator(()ident(Section)operator(.)ident(ANSWER)(\))
+ keyword(if) operator(()ident(answers)(\)) keyword(return) ident(answers)operator([)integer(0)(])operator(.)ident(rdataToString)operator(()(\)) keyword(else) keyword(return) ident(hostIp)
+(})
+ident(println) string<delimiter(')content(208.201.239.36 => )delimiter(')> operator(+) ident(reverseDns)operator(()string<delimiter(')content(208.201.239.36)delimiter(')>(\))
+comment(// => 208.201.239.36 => www.oreillynet.com.)
+
+keyword(def) method(hostAddrs)operator(()ident(name)(\)) operator({)
+ ident(addresses) operator(=) ident(Address)operator(.)ident(getAllByName)operator(()ident(name)(\))
+ ident(println) ident(addresses)operator([)integer(0)(])operator(.)ident(canonicalHostName) operator(+) string<delimiter(')content( => )delimiter(')> operator(+) ident(addresses)operator(.)ident(collect)operator({) local_variable(it)operator(.)ident(hostAddress) (})operator(.)ident(join)operator(()string<delimiter(')content( )delimiter(')>(\))
+(})
+ident(hostAddrs)operator(()string<delimiter(')content(www.ora.com)delimiter(')>(\))
+comment(// => www.oreillynet.com. => 208.201.239.36 208.201.239.37)
+ident(hostAddrs)operator(()string<delimiter(')content(www.whitehouse.gov)delimiter(')>(\))
+comment(// => 61.9.209.153 => 61.9.209.153 61.9.209.151)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_18.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// commons net examples (explicit error handling not shown\))
+keyword(import) include(java.text.DateFormat)
+keyword(import) include(org.apache.commons.net.ftp.FTPClient)
+comment(// connect)
+ident(server) operator(=) string<delimiter(")content(localhost)delimiter(")> comment(//server = "ftp.host.com")
+
+ident(ftp) operator(=) keyword(new) ident(FTPClient)operator(()(\))
+ident(ftp)operator(.)ident(connect)operator(() ident(server) (\))
+ident(ftp)operator(.)ident(login)operator(() string<delimiter(')content(anonymous)delimiter(')>operator(,) string<delimiter(')content(guest)delimiter(')> (\)) comment(//ftp.login( 'username', 'password' \))
+
+ident(println) string<delimiter(")content(Connected to )inline<inline_delimiter($)ident(server)>content(. )inline<inline_delimiter($)ident(ftp)>content(.replyString)delimiter(")>
+
+comment(// retrieve file)
+ident(ftp)operator(.)ident(changeWorkingDirectory)operator(() string<delimiter(')content(.)delimiter(')> (\)) comment(//ftp.changeWorkingDirectory( 'serverFolder' \))
+ident(file) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(README.txt)delimiter(')>(\)) comment(//new File('localFolder' + File.separator + 'localFilename'\))
+
+ident(file)operator(.)ident(withOutputStream)operator({) ident(os) operator(->)
+ ident(ftp)operator(.)ident(retrieveFile)operator(() string<delimiter(')content(README.txt)delimiter(')>operator(,) ident(os) (\)) comment(//ftp.retrieveFile( 'serverFilename', os \))
+(})
+
+comment(// upload file)
+ident(file) operator(=) keyword(new) pre_type(File)operator(()string<delimiter(')content(otherFile.txt)delimiter(')>(\)) comment(//new File('localFolder' + File.separator + 'localFilename'\))
+ident(file)operator(.)ident(withInputStream)operator({) ident(fis) operator(->) ident(ftp)operator(.)ident(storeFile)operator(() string<delimiter(')content(otherFile.txt)delimiter(')>operator(,) ident(fis) (\)) (})
+
+comment(// List the files in the directory)
+ident(files) operator(=) ident(ftp)operator(.)ident(listFiles)operator(()(\))
+ident(println) string<delimiter(")content(Number of files in dir: )inline<inline_delimiter($)ident(files)>content(.length)delimiter(")>
+ident(df) operator(=) pre_type(DateFormat)operator(.)ident(getDateInstance)operator(() pre_type(DateFormat)operator(.)ident(SHORT) (\))
+ident(files)operator(.)ident(each)operator({) ident(file) operator(->)
+ ident(println) string<delimiter(")inline<inline_delimiter(${)ident(df.format(file.timestamp.time\))inline_delimiter(})>char(\\t)content( )inline<inline_delimiter($)ident(file)>content(.name)delimiter(")>
+(})
+
+comment(// Logout from the FTP Server and disconnect)
+ident(ftp)operator(.)ident(logout)operator(()(\))
+ident(ftp)operator(.)ident(disconnect)operator(()(\))
+comment(// =>)
+comment(// Connected to localhost. 230 User logged in, proceed.)
+comment(// Number of files in dir: 2)
+comment(// 18/01/07 otherFile.txt)
+comment(// 25/04/06 README.txt)
+
+
+comment(// Using AntBuilder; for more details, see:)
+comment(// http://ant.apache.org/manual/OptionalTasks/ftp.html)
+ident(ant) operator(=) keyword(new) ident(AntBuilder)operator(()(\))
+ident(ant)operator(.)ident(ftp)operator(()key(action)operator(:)string<delimiter(')content(send)delimiter(')>operator(,) key(server)operator(:)string<delimiter(')content(ftp.hypothetical.india.org)delimiter(')>operator(,) key(port)operator(:)string<delimiter(')content(2121)delimiter(')>operator(,)
+ key(remotedir)operator(:)string<delimiter(')content(/pub/incoming)delimiter(')>operator(,) key(userid)operator(:)string<delimiter(')content(coder)delimiter(')>operator(,) key(password)operator(:)string<delimiter(')content(java1)delimiter(')>operator(,)
+ key(depends)operator(:)string<delimiter(')content(yes)delimiter(')>operator(,) key(binary)operator(:)string<delimiter(')content(no)delimiter(')>operator(,) key(systemTypeKey)operator(:)string<delimiter(')content(Windows)delimiter(')>operator(,)
+ key(serverTimeZoneConfig)operator(:)string<delimiter(')content(India/Calcutta)delimiter(')>(\))operator({)
+ ident(fileset)operator(()key(dir)operator(:)string<delimiter(')content(htdocs/manual)delimiter(')>(\))operator({)
+ ident(include)operator(()key(name)operator(:)string<delimiter(')content(**/*.html)delimiter(')>(\))
+ (})
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_18.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// using AntBuilder; for more info, see:)
+comment(// http://ant.apache.org/manual/CoreTasks/mail.html)
+ident(ant) operator(=) keyword(new) ident(AntBuilder)operator(()(\))
+ident(ant)operator(.)ident(mail)operator(()key(mailhost)operator(:)string<delimiter(')content(smtp.myisp.com)delimiter(')>operator(,) key(mailport)operator(:)string<delimiter(')content(1025)delimiter(')>operator(,) key(subject)operator(:)string<delimiter(')content(Test build)delimiter(')>(\))operator({)
+ ident(from)operator(()key(address)operator(:)string<delimiter(')content(config@myisp.com)delimiter(')>(\))
+ ident(replyto)operator(()key(address)operator(:)string<delimiter(')content(me@myisp.com)delimiter(')>(\))
+ ident(to)operator(()key(address)operator(:)string<delimiter(')content(all@xyz.com)delimiter(')>(\))
+ ident(message)operator(()string<delimiter(")content(The )inline<inline_delimiter(${)ident(buildname)inline_delimiter(})>content( nightly build has completed)delimiter(")>(\))
+ ident(attachments)operator(()(\))operator({) comment(// ant 1.7 uses files attribute in earlier versions)
+ ident(fileset)operator(()key(dir)operator(:)string<delimiter(')content(dist)delimiter(')>(\))operator({)
+ ident(include)operator(()key(name)operator(:)string<delimiter(')content(**/*.zip)delimiter(')>(\))
+ (})
+ (})
+(})
+
+comment(// using commons net)
+keyword(import) include(org.apache.commons.net.smtp.*)
+ident(client) operator(=) keyword(new) ident(SMTPClient)operator(()(\))
+ident(client)operator(.)ident(connect)operator(() string<delimiter(")content(mail.myserver.com)delimiter(")>operator(,) integer(25) (\))
+keyword(if)operator(() operator(!)ident(SMTPReply)operator(.)ident(isPositiveCompletion)operator(()ident(client)operator(.)ident(replyCode)(\)) (\)) operator({)
+ ident(client)operator(.)ident(disconnect)operator(()(\))
+ pre_type(System)operator(.)ident(err)operator(.)ident(println)operator(()string<delimiter(")content(SMTP server refused connection.)delimiter(")>(\))
+ pre_type(System)operator(.)ident(exit)operator(()integer(1)(\))
+(})
+
+comment(// Login)
+ident(client)operator(.)ident(login)operator(() string<delimiter(")content(myserver.com)delimiter(")> (\))
+
+comment(// Set the sender and recipient(s\))
+ident(client)operator(.)ident(setSender)operator(() string<delimiter(")content(config@myisp.com)delimiter(")> (\))
+ident(client)operator(.)ident(addRecipient)operator(() string<delimiter(")content(all@xyz.com)delimiter(")> (\))
+
+comment(// Use the SimpleSMTPHeader class to build the header)
+ident(writer) operator(=) keyword(new) pre_type(PrintWriter)operator(() ident(client)operator(.)ident(sendMessageData)operator(()(\)) (\))
+ident(header) operator(=) keyword(new) ident(SimpleSMTPHeader)operator(() string<delimiter(")content(config@myisp.com)delimiter(")>operator(,) string<delimiter(")content(all@xyz.com)delimiter(")>operator(,) string<delimiter(")content(My Subject)delimiter(")>(\))
+ident(header)operator(.)ident(addCC)operator(() string<delimiter(")content(me@myisp.com)delimiter(")> (\))
+ident(header)operator(.)ident(addHeaderField)operator(() string<delimiter(")content(Organization)delimiter(")>operator(,) string<delimiter(")content(My Company)delimiter(")> (\))
+
+comment(// Write the header to the SMTP Server)
+ident(writer)operator(.)ident(write)operator(() ident(header)operator(.)ident(toString)operator(()(\)) (\))
+
+comment(// Write the body of the message)
+ident(writer)operator(.)ident(write)operator(() string<delimiter(")content(This is a test...)delimiter(")> (\))
+
+comment(// Close the writer )
+ident(writer)operator(.)ident(close)operator(()(\))
+keyword(if) operator(() operator(!)ident(client)operator(.)ident(completePendingCommand)operator(()(\)) (\)) comment(// failure)
+ pre_type(System)operator(.)ident(exit)operator(() integer(1) (\))
+
+comment(// Logout from the e-mail server (QUIT\) and close connection)
+ident(client)operator(.)ident(logout)operator(()(\))
+ident(client)operator(.)ident(disconnect)operator(()(\))
+
+comment(// You can also use JavaMail; for more details, see:)
+comment(// http://java.sun.com/products/javamail/)
+
+comment(// For testing programs which send emails, consider:)
+comment(// Dumbster (http://quintanasoft.com/dumbster/\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_18.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// slight variation to original cookbook:)
+comment(// prints 1st, 2nd and last articles from random newsgroup)
+keyword(import) include(org.apache.commons.net.nntp.NNTPClient)
+ident(postingPerm) operator(=) operator([)string<delimiter(')content(Unknown)delimiter(')>operator(,) string<delimiter(')content(Moderated)delimiter(')>operator(,) string<delimiter(')content(Permitted)delimiter(')>operator(,) string<delimiter(')content(Prohibited)delimiter(')>(])
+ident(client) operator(=) keyword(new) ident(NNTPClient)operator(()(\))
+ident(client)operator(.)ident(connect)operator(()string<delimiter(")content(news.example.com)delimiter(")>(\))
+ident(list) operator(=) ident(client)operator(.)ident(listNewsgroups)operator(()(\))
+ident(println) string<delimiter(")content(Found )inline<inline_delimiter(${)ident(list.size(\))inline_delimiter(})>content( newsgroups)delimiter(")>
+ident(aList) operator(=) ident(list)operator([)keyword(new) pre_type(Random)operator(()(\))operator(.)ident(nextInt)operator(()ident(list)operator(.)ident(size)operator(()(\)\)])
+ident(println) string<delimiter(")inline<inline_delimiter($)ident(aList)>content(.newsgroup has )inline<inline_delimiter($)ident(aList)>content(.articleCount articles)delimiter(")>
+ident(println) string<delimiter(")content(PostingPermission = )inline<inline_delimiter(${)ident(postingPerm[aList.postingPermission])inline_delimiter(})>delimiter(")>
+ident(first) operator(=) ident(aList)operator(.)ident(firstArticle)
+ident(println) string<delimiter(")content(First=)inline<inline_delimiter($)ident(first)>content(, Last=)inline<inline_delimiter($)ident(aList)>content(.lastArticle)delimiter(")>
+ident(client)operator(.)ident(retrieveArticle)operator(()ident(first)(\))operator(?)operator(.)ident(eachLine)operator({) ident(println) local_variable(it) (})
+ident(client)operator(.)ident(selectNextArticle)operator(()(\))
+ident(client)operator(.)ident(retrieveArticle)operator(()(\))operator(?)operator(.)ident(eachLine)operator({) ident(println) local_variable(it) (})
+ident(client)operator(.)ident(retrieveArticle)operator(()ident(aList)operator(.)ident(lastArticle)(\))operator(?)operator(.)ident(eachLine)operator({) ident(println) local_variable(it) (})
+ident(writer) operator(=) ident(client)operator(.)ident(postArticle)operator(()(\))
+comment(// ... use writer ...)
+ident(writer)operator(.)ident(close)operator(()(\))
+ident(client)operator(.)ident(logout)operator(()(\))
+keyword(if) operator(()ident(client)operator(.)ident(isConnected)operator(()(\)\)) ident(client)operator(.)ident(disconnect)operator(()(\))
+comment(// =>)
+comment(// Found 37025 newsgroups)
+comment(// alt.comp.sys.palmtops.pilot has 730 articles)
+comment(// PostingPermission = Permitted)
+comment(// First=21904, Last=22633)
+comment(// ...)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_18.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// slight variation to original cookbook to print summary of messages on server)
+comment(// uses commons net)
+keyword(import) include(org.apache.commons.net.pop3.POP3Client)
+ident(server) operator(=) string<delimiter(')content(pop.myisp.com)delimiter(')>
+ident(username) operator(=) string<delimiter(')content(gnat)delimiter(')>
+ident(password) operator(=) string<delimiter(')content(S33kr1T Pa55w0rD)delimiter(')>
+ident(timeoutMillis) operator(=) integer(30000)
+
+keyword(def) method(printMessageInfo)operator(()ident(reader)operator(,) ident(id)(\)) operator({)
+ keyword(def) ident(from)operator(,) ident(subject)
+ ident(reader)operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ ident(lower) operator(=) ident(line)operator(.)ident(toLowerCase)operator(()(\))
+ keyword(if) operator(()ident(lower)operator(.)ident(startsWith)operator(()string<delimiter(")content(from: )delimiter(")>(\)\)) ident(from) operator(=) ident(line)operator([)integer(6)operator(..)operator(-)integer(1)(])operator(.)ident(trim)operator(()(\))
+ keyword(else) keyword(if) operator(()ident(lower)operator(.)ident(startsWith)operator(()string<delimiter(")content(subject: )delimiter(")>(\)\)) ident(subject) operator(=) ident(line)operator([)integer(9)operator(..)operator(-)integer(1)(])operator(.)ident(trim)operator(()(\))
+ (})
+ ident(println) string<delimiter(")inline<inline_delimiter($)ident(id)>content( From: )inline<inline_delimiter($)ident(from)>content(, Subject: )inline<inline_delimiter($)ident(subject)>delimiter(")>
+(})
+
+ident(pop3) operator(=) keyword(new) ident(POP3Client)operator(()(\))
+ident(pop3)operator(.)ident(setDefaultTimeout)operator(()ident(timeoutMillis)(\))
+ident(pop3)operator(.)ident(connect)operator(()ident(server)(\))
+
+keyword(if) operator(()operator(!)ident(pop3)operator(.)ident(login)operator(()ident(username)operator(,) ident(password)(\)\)) operator({)
+ pre_type(System)operator(.)ident(err)operator(.)ident(println)operator(()string<delimiter(")content(Could not login to server. Check password.)delimiter(")>(\))
+ ident(pop3)operator(.)ident(disconnect)operator(()(\))
+ pre_type(System)operator(.)ident(exit)operator(()integer(1)(\))
+(})
+ident(messages) operator(=) ident(pop3)operator(.)ident(listMessages)operator(()(\))
+keyword(if) operator(()operator(!)ident(messages)(\)) pre_type(System)operator(.)ident(err)operator(.)ident(println)operator(()string<delimiter(")content(Could not retrieve message list.)delimiter(")>(\))
+keyword(else) keyword(if) operator(()ident(messages)operator(.)ident(length) operator(==) integer(0)(\)) ident(println)operator(()string<delimiter(")content(No messages)delimiter(")>(\))
+keyword(else) operator({)
+ ident(messages)operator(.)ident(each)operator({) ident(message) operator(->)
+ ident(reader) operator(=) ident(pop3)operator(.)ident(retrieveMessageTop)operator(()ident(message)operator(.)ident(number)operator(,) integer(0)(\))
+ keyword(if) operator(()operator(!)ident(reader)(\)) operator({)
+ pre_type(System)operator(.)ident(err)operator(.)ident(println)operator(()string<delimiter(")content(Could not retrieve message header. Skipping...)delimiter(")>(\))
+ (})
+ ident(printMessageInfo)operator(()keyword(new) pre_type(BufferedReader)operator(()ident(reader)(\))operator(,) ident(message)operator(.)ident(number)(\))
+ (})
+(})
+
+ident(pop3)operator(.)ident(logout)operator(()(\))
+ident(pop3)operator(.)ident(disconnect)operator(()(\))
+
+comment(// You can also use JavaMail; for more details, see:)
+comment(// http://java.sun.com/products/javamail/)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_18.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// Variation to original cookbook: this more extensive example)
+comment(// uses telnet to extract weather information about Sydney from)
+comment(// a telnet-based weather server at the University of Michigan.)
+keyword(import) include(org.apache.commons.net.telnet.TelnetClient)
+
+keyword(def) method(readUntil)operator(() ident(pattern) (\)) operator({)
+ ident(sb) operator(=) keyword(new) pre_type(StringBuffer)operator(()(\))
+ keyword(while) operator(()operator(()ident(ch) operator(=) ident(reader)operator(.)ident(read)operator(()(\)\)) operator(!=) operator(-)integer(1)(\)) operator({)
+ ident(sb) operator(<)operator(<) operator(()type(char)(\)) ident(ch)
+ keyword(if) operator(()ident(sb)operator(.)ident(toString)operator(()(\))operator(.)ident(endsWith)operator(()ident(pattern)(\)\)) operator({)
+ keyword(def) ident(found) operator(=) ident(sb)operator(.)ident(toString)operator(()(\))
+ ident(sb) operator(=) keyword(new) pre_type(StringBuffer)operator(()(\))
+ keyword(return) ident(found)
+ (})
+ (})
+ keyword(return) keyword(null)
+(})
+
+ident(telnet) operator(=) keyword(new) ident(TelnetClient)operator(()(\))
+ident(telnet)operator(.)ident(connect)operator(() string<delimiter(')content(rainmaker.wunderground.com)delimiter(')>operator(,) integer(3000) (\))
+ident(reader) operator(=) ident(telnet)operator(.)ident(inputStream)operator(.)ident(newReader)operator(()(\))
+ident(writer) operator(=) keyword(new) pre_type(PrintWriter)operator(()keyword(new) pre_type(OutputStreamWriter)operator(()ident(telnet)operator(.)ident(outputStream)(\))operator(,)keyword(true)(\))
+ident(readUntil)operator(() string<delimiter(")content(Welcome)delimiter(")> (\))
+ident(println) string<delimiter(')content(Welcome)delimiter(')> operator(+) ident(readUntil)operator(() string<delimiter(")content(!)delimiter(")> (\))
+ident(readUntil)operator(() string<delimiter(")content(continue:)delimiter(")> (\))
+ident(writer)operator(.)ident(println)operator(()(\))
+ident(readUntil)operator(() string<delimiter(")content(-- )delimiter(")> (\))
+ident(writer)operator(.)ident(println)operator(()(\))
+ident(readUntil)operator(() string<delimiter(")content(Selection:)delimiter(")> (\))
+ident(writer)operator(.)ident(println)operator(()string<delimiter(")content(10)delimiter(")>(\))
+ident(readUntil)operator(() string<delimiter(")content(Selection:)delimiter(")> (\))
+ident(writer)operator(.)ident(println)operator(()string<delimiter(")content(3)delimiter(")>(\))
+ident(x) operator(=) ident(readUntil)operator(() string<delimiter(")content(Return)delimiter(")> (\))
+keyword(while) operator(()operator(!)ident(x)operator(.)ident(contains)operator(()string<delimiter(')content(SYDNEY)delimiter(')>(\)\)) operator({)
+ ident(writer)operator(.)ident(println)operator(()(\))
+ ident(x) operator(=) ident(readUntil)operator(() string<delimiter(")content(Return)delimiter(")> (\))
+(})
+ident(m) operator(=) operator(()ident(x) operator(=~) regexp<delimiter(/)content((?sm\).*(SYDNEY.*?\))content($)delimiter(/)>(\))
+ident(telnet)operator(.)ident(disconnect)operator(()(\))
+ident(println) ident(m)operator([)integer(0)(])operator([)integer(1)(])
+comment(// =>)
+comment(// Welcome to THE WEATHER UNDERGROUND telnet service!)
+comment(// SYDNEY FAIR 10AM 81 27)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_18.7)
+comment(//----------------------------------------------------------------------------------)
+ident(address) operator(=) pre_type(InetAddress)operator(.)ident(getByName)operator(()string<delimiter(")content(web.mit.edu)delimiter(")>(\))
+ident(timeoutMillis) operator(=) integer(3000)
+ident(println) ident(address)operator(.)ident(isReachable)operator(()ident(timeoutMillis)(\))
+comment(// => true (if firewalls don't get in the way, may require privileges on Linux,)
+comment(// may not use ICMP but rather Echo protocol on Windows machines\))
+
+comment(// You can also use commons net EchoUDPClient and EchoTCPClient to interact)
+comment(// with the Echo protocol - sometimes useful for ping-like functionality.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_18.8)
+comment(//----------------------------------------------------------------------------------)
+keyword(import) include(org.apache.commons.net.WhoisClient)
+ident(whois) operator(=) keyword(new) ident(WhoisClient)operator(()(\))
+ident(whois)operator(.)ident(connect)operator(()ident(WhoisClient)operator(.)ident(DEFAULT_HOST)(\))
+ident(result) operator(=) ident(whois)operator(.)ident(query)operator(()string<delimiter(')content(cnn.com)delimiter(')>(\)) comment(// as text of complete query)
+ident(println) ident(result) comment(// could extract info from result here (using e.g. regex\))
+ident(whois)operator(.)ident(disconnect)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_18.9)
+comment(//----------------------------------------------------------------------------------)
+comment(// not exact equivalent to original cookbook: just shows raw functionality)
+ident(client) operator(=) keyword(new) ident(SMTPClient)operator(()(\))
+ident(client)operator(.)ident(connect)operator(() string<delimiter(")content(smtp.example.com)delimiter(")>operator(,) integer(25) (\))
+ident(println) ident(client)operator(.)ident(verify)operator(()string<delimiter(")content(george)delimiter(")>(\)) comment(// => true)
+ident(println) ident(client)operator(.)ident(replyString) comment(// => 250 George Washington <george@wash.dc.gov>)
+ident(println) ident(client)operator(.)ident(verify)operator(()string<delimiter(")content(jetson)delimiter(")>(\)) comment(// => false)
+ident(println) ident(client)operator(.)ident(replyString) comment(// => 550 jetson... User unknown)
+ident(client)operator(.)ident(expn)operator(()string<delimiter(")content(presidents)delimiter(")>(\))
+ident(println) ident(client)operator(.)ident(replyString)
+comment(// =>)
+comment(// 250-George Washington <george@wash.dc.gov>)
+comment(// 250-Thomas Jefferson <tj@wash.dc.gov>)
+comment(// 250-Ben Franklin <ben@here.us.edu>)
+comment(// ...)
+
+comment(// expect these commands to be disabled by most public servers due to spam)
+ident(println) ident(client)operator(.)ident(replyString)
+comment(// => 502 Command is locally disabled)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.0)
+comment(//----------------------------------------------------------------------------------)
+comment(// URLs have the same form as in Perl)
+
+comment(// Invoking dynamic content is done through the same standard urls:)
+comment(// http://mox.perl.com/cgi-bin/program?name=Johann&born=1685)
+comment(// http://mox.perl.com/cgi-bin/program)
+
+comment(// Groovy has Groovelets and GSP page support built-in. For a full)
+comment(// web framework, see Grails: http://grails.codehaus.org/)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.1)
+comment(//----------------------------------------------------------------------------------)
+comment(// as a plain groovelet)
+ident(param) operator(=) ident(request)operator(.)ident(getParameter)operator(()string<delimiter(')content(PARAM_NAME)delimiter(')>(\))
+ident(println) string<delimiter(""")content(
+<html><head>
+<title>Howdy there!</title>
+</head>
+<body>
+<p>
+You typed: )inline<inline_delimiter($)ident(param)>content(
+</p>
+</body>
+</html>
+)delimiter(""")>
+
+comment(// as a groovelet using markup builder)
+keyword(import) include(groovy.xml.MarkupBuilder)
+ident(writer) operator(=) keyword(new) pre_type(StringWriter)operator(()(\))
+ident(builder) operator(=) keyword(new) ident(MarkupBuilder)operator(()ident(writer)(\))
+ident(builder)operator(.)ident(html) operator({)
+ ident(head) operator({)
+ ident(title) string<delimiter(')content(Howdy there!)delimiter(')>
+ (})
+ ident(body) operator({)
+ ident(p)operator(()string<delimiter(')content(You typed: )delimiter(')> operator(+) ident(request)operator(.)ident(getParameter)operator(()string<delimiter(')content(PARAM_NAME)delimiter(')>(\)\))
+ (})
+(})
+ident(println) ident(writer)operator(.)ident(toString)operator(()(\))
+
+comment(// as a GSP page:)
+operator(<)ident(html)operator(>)operator(<)ident(head)operator(>)
+operator(<)ident(title)operator(>)ident(Howdy) ident(there)operator(!)operator(<)regexp<delimiter(/)content(title>)>error()
+operator(<)regexp<delimiter(/)content(head>)>error()
+operator(<)ident(body)operator(>)
+operator(<)ident(p)operator(>)
+ident(You) key(typed)operator(:) error($)operator({)ident(request)operator(.)ident(getParameter)operator(()string<delimiter(')content(PARAM_NAME)delimiter(')>(\)})
+operator(<)regexp<delimiter(/)content(p>)>error()
+operator(<)regexp<delimiter(/)content(body>)>error()
+operator(<)regexp<delimiter(/)content(html>)>error()
+
+comment(// Request parameters are often encoded by the browser before)
+comment(// sending to the server and usually can be printed out as is.)
+comment(// If you need to convert, use commons lang StringEscapeUtils#escapeHtml(\))
+comment(// and StringEscapeUtils#unescapeHtml(\).)
+
+comment(// Getting parameters:)
+ident(who) operator(=) ident(request)operator(.)ident(getParameter)operator(()string<delimiter(')content(Name)delimiter(')>(\))
+ident(phone) operator(=) ident(request)operator(.)ident(getParameter)operator(()string<delimiter(')content(Number)delimiter(')>(\))
+ident(picks) operator(=) ident(request)operator(.)ident(getParameterValues)operator(()string<delimiter(')content(Choices)delimiter(')>(\)) comment(// String array or null)
+
+comment(// Changing headers:)
+ident(response)operator(.)ident(setContentType)operator(()string<delimiter(')content(text/html;charset=UTF-8)delimiter(')>(\))
+ident(response)operator(.)ident(setContentType)operator(()string<delimiter(')content(text/plain)delimiter(')>(\))
+ident(response)operator(.)ident(setContentType)operator(()string<delimiter(')content(text/plain)delimiter(')>(\))
+ident(response)operator(.)ident(setHeader)operator(()string<delimiter(')content(Cache-control)delimiter(')>operator(,) string<delimiter(')content(no-cache)delimiter(')>(\))
+ident(response)operator(.)ident(setDateHeader)operator(()string<delimiter(')content(Expires)delimiter(')>operator(,) pre_type(System)operator(.)ident(currentTimeMillis)operator(()(\)) operator(+) integer(3)operator(*)integer(24)operator(*)integer(60)operator(*)integer(60)operator(*)integer(1000)(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// The Java Servlet API has a special log(\) method for writing to the)
+comment(// web server log.)
+
+comment(// To send errors to custom HTML pages, update the web.xml deployment)
+comment(// descriptor to include one or more <error-page> elements, e.g.:)
+operator(<)ident(error)operator(-)ident(page)operator(>)
+ operator(<)ident(error)operator(-)ident(code)operator(>)integer(404)operator(<)regexp<delimiter(/)content(error-code>)>error()
+ operator(<)ident(location)operator(>)regexp<delimiter(/)content(404.html<)delimiter(/)>ident(location)operator(>)
+operator(<)regexp<delimiter(/)content(error-page>)>error()
+operator(<)ident(error)operator(-)ident(page)operator(>)
+ operator(<)ident(exception)operator(-)ident(type)operator(>)ident(java)operator(.)ident(lang)operator(.)ident(NullPointerException)operator(<)regexp<delimiter(/)content(exception-type>)>error()
+ operator(<)ident(location)operator(>)regexp<delimiter(/)content(NpeError.gsp<)delimiter(/)>ident(location)operator(>)
+operator(<)regexp<delimiter(/)content(error-page>)>error()
+
+comment(// Another trick is to catch an exception within the servlet/gsp code)
+comment(// and print it out into the HTML as a comment.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// 500 errors could occur if you have compile errors in your script.)
+comment(// Pre-compile with your IDE or groovyc.)
+
+comment(// You can use an expando, mock or map to run your scripts outside)
+comment(// the web container environment. If you use Jetty as your container)
+comment(// it has a special servlet tester, for more details:)
+comment(// http://blogs.webtide.com/gregw/2006/12/16/1166307599250.html)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// Web servers should be invoked with an appropriate Java security policy in place.)
+comment(// This can be used to limit possible actions from hacking attempts.)
+
+comment(// Normal practices limit hacking exposure. The JDBC API encourages the use)
+comment(// of Prepared queries rather than encouraging practices which lead to SQL)
+comment(// injection. Using system or exec is rarely used either as Java provides)
+comment(// cross-platform mechanisms for most operating system level functionality.)
+
+comment(// Other security measures should be complemented with SSL and authentication.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// Within the servlet element of your web.xml, there is a <load-on-startup> element.)
+comment(// Use that on a per servlet basis to pre-load whichever servlets you like.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// As discussed in 19.3 and 19.4:)
+comment(// Web servers should be invoked with an appropriate Java security policy in place.)
+comment(// This can be used to limit possible actions from hacking attempts.)
+
+comment(// Normal practices limit hacking exposure. The JDBC API encourages the use)
+comment(// of Prepared queries rather than encouraging practices which lead to SQL)
+comment(// injection. Using system or exec is rarely used either as Java provides)
+comment(// cross-platform mechanisms for most operating system level functionality.)
+
+comment(// In addition, if authentication is used, security can be locked down at a)
+comment(// very fine-grained level on a per servlet action or per user (with JAAS\) basis.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.7)
+comment(//----------------------------------------------------------------------------------)
+keyword(import) include(groovy.xml.*)
+comment(// using a builder:)
+ident(Closure) ident(markup) operator(=) operator({)
+ ident(ol) operator({)
+ operator([)string<delimiter(')content(red)delimiter(')>operator(,)string<delimiter(')content(blue)delimiter(')>operator(,)string<delimiter(')content(green)delimiter(')>(])operator(.)ident(each)operator({) ident(li)operator(()local_variable(it)(\)) (})
+ (})
+(})
+ident(println) keyword(new) ident(StreamingMarkupBuilder)operator(()(\))operator(.)ident(bind)operator(()ident(markup)(\))operator(.)ident(toString)operator(()(\))
+comment(// => <ol><li>red</li><li>blue</li><li>green</li></ol>)
+
+ident(names) operator(=) string<delimiter(')content(Larry Moe Curly)delimiter(')>operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))
+ident(markup) operator(=) operator({)
+ ident(ul) operator({)
+ ident(names)operator(.)ident(each)operator({) ident(li)operator(()key(type)operator(:)string<delimiter(')content(disc)delimiter(')>operator(,) local_variable(it)(\)) (})
+ (})
+(})
+ident(println) keyword(new) ident(StreamingMarkupBuilder)operator(()(\))operator(.)ident(bind)operator(()ident(markup)(\))operator(.)ident(toString)operator(()(\))
+comment(// <ul><li type="disc">Larry</li><li type="disc">Moe</li>)
+comment(// <li type="disc">Curly</li></ul>)
+comment(//-----------------------------)
+
+ident(m) operator(=) operator({) ident(li)operator(()string<delimiter(")content(alpha)delimiter(")>(\)) (})
+ident(println) keyword(new) ident(StreamingMarkupBuilder)operator(()(\))operator(.)ident(bind)operator(()ident(m)(\))operator(.)ident(toString)operator(()(\))
+comment(// <li>alpha</li>)
+
+ident(m) operator(=) operator({) operator([)string<delimiter(')content(alpha)delimiter(')>operator(,)string<delimiter(')content(omega)delimiter(')>(])operator(.)ident(each) operator({) ident(li)operator(()local_variable(it)(\)) (}) (})
+ident(println) keyword(new) ident(StreamingMarkupBuilder)operator(()(\))operator(.)ident(bind)operator(()ident(m)(\))operator(.)ident(toString)operator(()(\))
+comment(// <li>alpha</li> <li>omega</li>)
+comment(//-----------------------------)
+
+ident(states) operator(=) operator([)
+ string<delimiter(")content(Wisconsin)delimiter(")>operator(:) operator([) string<delimiter(")content(Superior)delimiter(")>operator(,) string<delimiter(")content(Lake Geneva)delimiter(")>operator(,) string<delimiter(")content(Madison)delimiter(")> (])operator(,)
+ string<delimiter(")content(Colorado)delimiter(")>operator(:) operator([) string<delimiter(")content(Denver)delimiter(")>operator(,) string<delimiter(")content(Fort Collins)delimiter(")>operator(,) string<delimiter(")content(Boulder)delimiter(")> (])operator(,)
+ string<delimiter(")content(Texas)delimiter(")>operator(:) operator([) string<delimiter(")content(Plano)delimiter(")>operator(,) string<delimiter(")content(Austin)delimiter(")>operator(,) string<delimiter(")content(Fort Stockton)delimiter(")> (])operator(,)
+ string<delimiter(")content(California)delimiter(")>operator(:) operator([) string<delimiter(")content(Sebastopol)delimiter(")>operator(,) string<delimiter(")content(Santa Rosa)delimiter(")>operator(,) string<delimiter(")content(Berkeley)delimiter(")> (])operator(,)
+(])
+
+ident(writer) operator(=) keyword(new) pre_type(StringWriter)operator(()(\))
+ident(builder) operator(=) keyword(new) ident(MarkupBuilder)operator(()ident(writer)(\))
+ident(builder)operator(.)ident(table)operator({)
+ ident(caption)operator(()string<delimiter(')content(Cities I Have Known)delimiter(')>(\))
+ ident(tr)operator({) ident(th)operator(()string<delimiter(')content(State)delimiter(')>(\))operator(;) ident(th)operator(()key(colspan)operator(:)integer(3)operator(,) string<delimiter(')content(Cities)delimiter(')>(\)) (})
+ ident(states)operator(.)ident(keySet)operator(()(\))operator(.)ident(sort)operator(()(\))operator(.)ident(each)operator({) ident(state) operator(->)
+ ident(tr)operator({)
+ ident(th)operator(()ident(state)(\))
+ ident(states)operator([)ident(state)(])operator(.)ident(sort)operator(()(\))operator(.)ident(each)operator({) ident(td)operator(()local_variable(it)(\)) (})
+ (})
+ (})
+(})
+ident(println) ident(writer)operator(.)ident(toString)operator(()(\))
+comment(// =>)
+comment(// <table>)
+comment(// <caption>Cities I Have Known</caption>)
+comment(// <tr>)
+comment(// <th>State</th>)
+comment(// <th colspan='3'>Cities</th>)
+comment(// </tr>)
+comment(// <tr>)
+comment(// <th>California</th>)
+comment(// <td>Berkeley</td>)
+comment(// <td>Santa Rosa</td>)
+comment(// <td>Sebastopol</td>)
+comment(// </tr>)
+comment(// <tr>)
+comment(// <th>Colorado</th>)
+comment(// <td>Boulder</td>)
+comment(// <td>Denver</td>)
+comment(// <td>Fort Collins</td>)
+comment(// </tr>)
+comment(// <tr>)
+comment(// <th>Texas</th>)
+comment(// <td>Austin</td>)
+comment(// <td>Fort Stockton</td>)
+comment(// <td>Plano</td>)
+comment(// </tr>)
+comment(// <tr>)
+comment(// <th>Wisconsin</th>)
+comment(// <td>Lake Geneva</td>)
+comment(// <td>Madison</td>)
+comment(// <td>Superior</td>)
+comment(// </tr>)
+comment(// </table>)
+
+keyword(import) include(groovy.sql.Sql)
+keyword(import) include(groovy.xml.MarkupBuilder)
+
+ident(dbHandle) operator(=) keyword(null)
+ident(dbUrl) operator(=) string<delimiter(')content(jdbc:hsqldb:...)delimiter(')>
+keyword(def) method(getDb)operator(()(\))operator({)
+ keyword(if) operator(()ident(dbHandle)(\)) keyword(return) ident(dbHandle)
+ keyword(def) ident(source) operator(=) keyword(new) ident(org)operator(.)ident(hsqldb)operator(.)ident(jdbc)operator(.)ident(jdbcDataSource)operator(()(\))
+ ident(source)operator(.)ident(database) operator(=) ident(dbUrl)
+ ident(source)operator(.)ident(user) operator(=) string<delimiter(')content(sa)delimiter(')>
+ ident(source)operator(.)ident(password) operator(=) string<delimiter(')delimiter(')>
+ ident(dbHandle) operator(=) keyword(new) ident(Sql)operator(()ident(source)(\))
+ keyword(return) ident(dbHandle)
+(})
+
+keyword(def) method(findByLimit)operator(()ident(limit)(\)) operator({)
+ ident(db)operator(.)ident(rows) string<delimiter(")content(SELECT name,salary FROM employees where salary > )inline<inline_delimiter($)ident(limit)>delimiter(")>
+(})
+
+ident(limit) operator(=) ident(request)operator(.)ident(getParameter)operator(()string<delimiter(')content(LIMIT)delimiter(')>(\))
+ident(writer) operator(=) keyword(new) pre_type(StringWriter)operator(()(\))
+ident(builder) operator(=) keyword(new) ident(MarkupBuilder)operator(()ident(writer)(\))
+ident(builder)operator(.)ident(html) operator({)
+ ident(head) operator({) ident(title)operator(()string<delimiter(')content(Salary Query)delimiter(')>(\)) (})
+ ident(h1)operator(()string<delimiter(')content(Search)delimiter(')>(\))
+ ident(form)operator({)
+ ident(p)operator(()string<delimiter(')content(Enter minimum salary)delimiter(')>(\))
+ ident(input)operator(()key(type)operator(:)string<delimiter(')content(text)delimiter(')>operator(,) key(name)operator(:)string<delimiter(')content(LIMIT)delimiter(')>(\))
+ ident(input)operator(()key(type)operator(:)string<delimiter(')content(submit)delimiter(')>(\))
+ (})
+ keyword(if) operator(()ident(limit)(\)) operator({)
+ ident(h1)operator(()string<delimiter(')content(Results)delimiter(')>(\))
+ ident(table)operator(()key(border)operator(:)integer(1)(\))operator({)
+ ident(findByLimit)operator(()ident(limit)(\))operator(.)ident(each)operator({) ident(row) operator(->)
+ ident(tr)operator({) ident(td)operator(()ident(row)operator(.)ident(name)(\))operator(;) ident(td)operator(()ident(row)operator(.)ident(salary)(\)) (})
+ (})
+ (})
+ (})
+(})
+ident(println) ident(writer)operator(.)ident(toString)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.8)
+comment(//----------------------------------------------------------------------------------)
+comment(// The preferred way to redirect to resources within the web application:)
+ident(dispatcher) operator(=) ident(request)operator(.)ident(getRequestDispatcher)operator(()string<delimiter(')content(hello.gsp)delimiter(')>(\))
+ident(dispatcher)operator(.)ident(forward)operator(()ident(request)operator(,) ident(response)(\))
+comment(// Old versions of web containers allowed this mechanism to also redirect)
+comment(// to external resources but this was deemed a potential security risk.)
+
+comment(// The suggested way to external sites (less efficient for internal resources\):)
+ident(response)operator(.)ident(sendRedirect)operator(()string<delimiter(")content(http://www.perl.com/CPAN/)delimiter(")>(\))
+
+comment(// set cookie and forward)
+ident(oreo) operator(=) keyword(new) ident(Cookie)operator(()string<delimiter(')content(filling)delimiter(')>operator(,) string<delimiter(')content(vanilla creme)delimiter(')>(\))
+ident(THREE_MONTHS) operator(=) integer(3) operator(*) integer(30) operator(*) integer(24) operator(*) integer(60) operator(*) integer(60)
+ident(oreo)operator(.)ident(maxAge) operator(=) ident(THREE_MONTHS)
+ident(oreo)operator(.)ident(domain) operator(=) string<delimiter(')content(.pleac.sourceforge.net)delimiter(')>
+ident(whither) operator(=) string<delimiter(')content(http://pleac.sourceforge.net/pleac_ruby/cgiprogramming.html)delimiter(')>
+ident(response)operator(.)ident(addCookie)operator(()ident(oreo)(\))
+ident(response)operator(.)ident(sendRedirect)operator(()ident(whither)(\))
+
+comment(// forward based on user agent)
+ident(dir) operator(=) string<delimiter(')content(http://www.science.uva.nl/%7Emes/jargon)delimiter(')>
+ident(agent) operator(=) ident(request)operator(.)ident(getHeader)operator(()string<delimiter(')content(user-agent)delimiter(')>(\))
+ident(menu) operator(=) operator([)
+ operator([)regexp<delimiter(/)content(Mac)delimiter(/)>operator(,) string<delimiter(')content(m/macintrash.html)delimiter(')>(])operator(,)
+ operator([)regexp<delimiter(/)content(Win(dows \)?NT)delimiter(/)>operator(,) string<delimiter(')content(e/evilandrude.html)delimiter(')>(])operator(,)
+ operator([)regexp<delimiter(/)content(Win|MSIE|WebTV)delimiter(/)>operator(,) string<delimiter(')content(m/microslothwindows.html)delimiter(')>(])operator(,)
+ operator([)regexp<delimiter(/)content(Linux)delimiter(/)>operator(,) string<delimiter(')content(l/linux.html)delimiter(')>(])operator(,)
+ operator([)regexp<delimiter(/)content(HP-UX)delimiter(/)>operator(,) string<delimiter(')content(h/hpsux.html)delimiter(')>(])operator(,)
+ operator([)regexp<delimiter(/)content(SunOS)delimiter(/)>operator(,) string<delimiter(')content(s/scumos.html)delimiter(')>(])operator(,)
+(])
+ident(page) operator(=) string<delimiter(')content(a/aportraitofj.randomhacker.html)delimiter(')>
+ident(menu)operator(.)ident(each)operator({)
+ keyword(if) operator(()ident(agent) operator(=~) local_variable(it)operator([)integer(0)(]\)) ident(page) operator(=) local_variable(it)operator([)integer(1)(])
+(})
+ident(response)operator(.)ident(sendRedirect)operator(()string<delimiter(")inline<inline_delimiter($)ident(dir)>content(/)inline<inline_delimiter($)ident(page)>delimiter(")>(\))
+
+comment(// no response output)
+ident(response)operator(.)ident(sendError)operator(()integer(204)operator(,) string<delimiter(')content(No Response)delimiter(')>(\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.9)
+comment(//----------------------------------------------------------------------------------)
+comment(// Consider TCPMON or similar: http://ws.apache.org/commons/tcpmon/)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.10)
+comment(//----------------------------------------------------------------------------------)
+comment(// helper method)
+keyword(import) include(javax.servlet.http.Cookie)
+keyword(import) include(groovy.xml.MarkupBuilder)
+
+keyword(def) method(getCookieValue)operator(()ident(cookies)operator(,) ident(cookieName)operator(,) ident(defaultValue)(\)) operator({)
+ keyword(if) operator(()ident(cookies)(\)) keyword(for) operator(()ident(i) keyword(in) integer(0)operator(..<)ident(cookies)operator(.)ident(length)(\)) operator({)
+ keyword(if) operator(()ident(cookieName) operator(==) ident(cookies)operator([)ident(i)(])operator(.)ident(name)(\)) keyword(return) ident(cookies)operator([)ident(i)(])operator(.)ident(value)
+ (})
+ keyword(return) ident(defaultValue)
+(})
+
+ident(prefValue) operator(=) ident(getCookieValue)operator(()ident(request)operator(.)ident(cookies)operator(,) string<delimiter(')content(preference_name)delimiter(')>operator(,) string<delimiter(')content(default)delimiter(')>(\))
+ident(cookie) operator(=) keyword(new) ident(Cookie)operator(()string<delimiter(')content(preference name)delimiter(')>operator(,)string<delimiter(")content(whatever you'd like)delimiter(")>(\))
+ident(SECONDS_PER_YEAR) operator(=) integer(60)operator(*)integer(60)operator(*)integer(24)operator(*)integer(365)
+ident(cookie)operator(.)ident(maxAge) operator(=) ident(SECONDS_PER_YEAR) operator(*) integer(2)
+ident(response)operator(.)ident(addCookie)operator(()ident(cookie)(\))
+
+ident(cookname) operator(=) string<delimiter(')content(fav_ice_cream)delimiter(')>
+ident(favorite) operator(=) ident(request)operator(.)ident(getParameter)operator(()string<delimiter(')content(flavor)delimiter(')>(\))
+ident(tasty) operator(=) ident(getCookieValue)operator(()ident(request)operator(.)ident(cookies)operator(,) ident(cookname)operator(,) string<delimiter(')content(mint)delimiter(')>(\))
+
+ident(writer) operator(=) keyword(new) pre_type(StringWriter)operator(()(\))
+ident(builder) operator(=) keyword(new) ident(MarkupBuilder)operator(()ident(writer)(\))
+ident(builder)operator(.)ident(html) operator({)
+ ident(head) operator({) ident(title)operator(()string<delimiter(')content(Ice Cookies)delimiter(')>(\)) (})
+ ident(body) operator({)
+ ident(h1)operator(()string<delimiter(')content(Hello Ice Cream)delimiter(')>(\))
+ keyword(if) operator(()ident(favorite)(\)) operator({)
+ ident(p)operator(()string<delimiter(")content(You chose as your favorite flavor ')inline<inline_delimiter($)ident(favorite)>content('.)delimiter(")>(\))
+ ident(cookie) operator(=) keyword(new) ident(Cookie)operator(()ident(cookname)operator(,) ident(favorite)(\))
+ ident(ONE_HOUR) operator(=) integer(3600) comment(// secs)
+ ident(cookie)operator(.)ident(maxAge) operator(=) ident(ONE_HOUR)
+ ident(response)operator(.)ident(addCookie)operator(()ident(cookie)(\))
+ (}) keyword(else) operator({)
+ ident(hr)operator(()(\))
+ ident(form) operator({)
+ ident(p)operator(()string<delimiter(')content(Please select a flavor: )delimiter(')>(\))
+ ident(input)operator(()key(type)operator(:)string<delimiter(')content(text)delimiter(')>operator(,) key(name)operator(:)string<delimiter(')content(flavor)delimiter(')>operator(,) key(value)operator(:)ident(tasty)(\))
+ (})
+ ident(hr)operator(()(\))
+ (})
+ (})
+(})
+ident(println) ident(writer)operator(.)ident(toString)operator(()(\))
+comment(//----------------------------------------------------------------------------------)
+
+comment(// @@PLEAC@@_19.11)
+comment(//----------------------------------------------------------------------------------)
+keyword(import) include(groovy.xml.MarkupBuilder)
+comment(// On Linux systems replace with: "who".execute(\).text)
+ident(fakedWhoInput) operator(=) string<delimiter(''')content(
+root tty1 Nov 2 17:57
+hermie tty3 Nov 2 18:43
+hermie tty4 Nov 1 20:01
+sigmund tty2 Nov 2 18:08
+)delimiter(''')>operator(.)ident(trim)operator(()(\))operator(.)ident(split)operator(()regexp<delimiter(/)char(\\n)delimiter(/)>(\))
+ident(name) operator(=) ident(request)operator(.)ident(getParameter)operator(()string<delimiter(')content(WHO)delimiter(')>(\))
+keyword(if) operator(()operator(!)ident(name)(\)) ident(name) operator(=) string<delimiter(')delimiter(')>
+ident(writer) operator(=) keyword(new) pre_type(StringWriter)operator(()(\))
+keyword(new) ident(MarkupBuilder)operator(()ident(writer)(\))operator(.)ident(html)operator({)
+ ident(head)operator({) ident(title)operator(()string<delimiter(')content(Query Users)delimiter(')>(\)) (})
+ ident(body)operator({)
+ ident(h1)operator(()string<delimiter(')content(Search)delimiter(')>(\))
+ ident(form)operator({)
+ ident(p)operator(()string<delimiter(')content(Which User?)delimiter(')>(\))
+ ident(input)operator(()key(type)operator(:)string<delimiter(')content(text)delimiter(')>operator(,) key(name)operator(:)string<delimiter(')content(WHO)delimiter(')>operator(,) key(value)operator(:)ident(name)(\))
+ ident(input)operator(()key(type)operator(:)string<delimiter(')content(submit)delimiter(')>(\))
+ (})
+ keyword(if) operator(()ident(name)(\)) operator({)
+ ident(h1)operator(()string<delimiter(')content(Results)delimiter(')>(\))
+ ident(lines) operator(=) ident(fakedWhoInput)operator(.)ident(grep)operator(()operator(~)regexp<delimiter(/)content(^)inline<inline_delimiter($)ident(name)>char(\\s)content(.*)delimiter(/)>(\))
+ keyword(if) operator(()ident(lines)(\)) ident(message) operator(=) ident(lines)operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+ keyword(else) ident(message) operator(=) string<delimiter(")inline<inline_delimiter($)ident(name)>content( is not logged in)delimiter(")>
+ ident(pre)operator(()ident(message)(\))
+ (})
+ (})
+(})
+ident(println) ident(writer)operator(.)ident(toString)operator(()(\))
+comment(// if you need to escape special symbols, e.g. '<' or '>' use commons lang StringEscapeUtils)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.12)
+comment(//----------------------------------------------------------------------------------)
+comment(// frameworks typically do this for you, but shown here are the manual steps)
+comment(// even when doing it manually, you would probably use session variables)
+
+comment(// setting a hidden field)
+ident(input)operator(()key(type)operator(:)string<delimiter(')content(hidden)delimiter(')>operator(,) key(value)operator(:)string<delimiter(')content(bacon)delimiter(')>(\))
+
+comment(// setting a value on the submit)
+ident(input)operator(()key(type)operator(:)string<delimiter(')content(submit)delimiter(')>operator(,) key(name)operator(:)string<delimiter(")content(.State)delimiter(")>operator(,) key(value)operator(:)string<delimiter(')content(Checkout)delimiter(')>(\))
+
+comment(// determining 'mode')
+ident(page) operator(=) ident(request)operator(.)ident(getParameter)operator(()string<delimiter(')content(.State)delimiter(')>(\))
+keyword(if) operator(()operator(!)ident(page)(\)) ident(page) operator(=) string<delimiter(')content(Default)delimiter(')>
+
+comment(// forking with if chain)
+keyword(if) operator(()ident(page) operator(==) string<delimiter(")content(Default)delimiter(")>(\)) operator({)
+ ident(frontPage)operator(()(\))
+(}) keyword(else) keyword(if) operator(()ident(page) operator(==) string<delimiter(")content(Checkout)delimiter(")>(\)) operator({)
+ ident(checkout)operator(()(\))
+(}) keyword(else) operator({)
+ ident(noSuchPage)operator(()(\))
+(})
+
+comment(// forking with map)
+ident(states) operator(=) operator([)
+ key(Default)operator(:) local_variable(this)operator(.)operator(&)ident(frontPage)operator(,)
+ key(Shirt)operator(:) local_variable(this)operator(.)operator(&)ident(tShirt)operator(,)
+ key(Sweater)operator(:) local_variable(this)operator(.)operator(&)ident(sweater)operator(,)
+ key(Checkout)operator(:) local_variable(this)operator(.)operator(&)ident(checkout)operator(,)
+ key(Card)operator(:) local_variable(this)operator(.)operator(&)ident(creditCard)operator(,)
+ key(Order)operator(:) local_variable(this)operator(.)operator(&)ident(order)operator(,)
+ key(Cancel)operator(:) local_variable(this)operator(.)operator(&)ident(frontPage)operator(,)
+(])
+
+comment(// calling each to allow hidden variable saving)
+ident(states)operator(.)ident(each)operator({) ident(key)operator(,) ident(closure) operator(->)
+ ident(closure)operator(()ident(page) operator(==) ident(key)(\))
+(})
+
+comment(// exemplar method)
+keyword(def) method(tShirt)operator(()ident(active)(\)) operator({)
+ keyword(def) ident(sizes) operator(=) operator([)string<delimiter(')content(XL)delimiter(')>operator(,) string<delimiter(')content(L)delimiter(')>operator(,) string<delimiter(')content(M)delimiter(')>operator(,) string<delimiter(')content(S)delimiter(')>(])
+ keyword(def) ident(colors) operator(=) operator([)string<delimiter(')content(Black)delimiter(')>operator(,) string<delimiter(')content(White)delimiter(')>(])
+ keyword(if) operator(()operator(!)ident(active)(\)) operator({)
+ ident(hidden)operator(()string<delimiter(")content(size)delimiter(")>(\))
+ ident(hidden)operator(()string<delimiter(")content(color)delimiter(")>(\))
+ keyword(return)
+ (})
+ ident(p)operator(()string<delimiter(")content(You want to buy a t-shirt?)delimiter(")>(\))operator(;)
+ ident(label)operator(()string<delimiter(")content(Size: )delimiter(")>(\))operator(;) ident(dropDown)operator(()string<delimiter(")content(size)delimiter(")>operator(,) ident(sizes)(\))
+ ident(label)operator(()string<delimiter(")content(Color: )delimiter(")>(\))operator(;) ident(dropDown)operator(()string<delimiter(")content(color)delimiter(")>operator(,) ident(colors)(\))
+ ident(shopMenu)operator(()(\))
+(})
+
+comment(// kicking off processing)
+ident(html)operator({)
+ ident(head)operator({) ident(title)operator(()string<delimiter(')content(chemiserie store)delimiter(')>(\)) (})
+ ident(body) operator({)
+ keyword(if) operator(()ident(states)operator([)ident(page)(]\)) ident(process)operator(()ident(page)(\))
+ keyword(else) ident(noSuchPage)operator(()(\))
+ (})
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// get request parameters as map)
+ident(map) operator(=) ident(request)operator(.)ident(parameterMap)
+
+comment(// save to file)
+keyword(new) pre_type(File)operator(()ident(filename)(\))operator(.)ident(withOutputStream)operator({) ident(fos) operator(->)
+ ident(oos) operator(=) keyword(new) pre_type(ObjectOutputStream)operator(()ident(fos)(\))
+ ident(oos)operator(.)ident(writeObject)operator(()ident(map)(\))
+ ident(oos)operator(.)ident(close)operator(()(\))
+(})
+
+comment(// convert to text)
+ident(sb) operator(=) keyword(new) pre_type(StringBuffer)operator(()(\))
+ident(map)operator(.)ident(each)operator({) ident(k)operator(,)ident(v) operator(->) ident(sb) operator(<)operator(<) string<delimiter(")inline<inline_delimiter($)ident(k)>content(=)inline<inline_delimiter($)ident(v)>delimiter(")> (})
+ident(text) operator(=) ident(sb)operator(.)ident(toString)operator(()(\))
+comment(// to send text via email, see 18.3)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_19.14)
+comment(//----------------------------------------------------------------------------------)
+comment(// you wouldn't normally do it this way, consider a framework like Grails)
+comment(// even when doing it by hand, you would probably use session variables)
+keyword(import) include(groovy.xml.MarkupBuilder)
+
+ident(page) operator(=) ident(param)operator(()string<delimiter(')content(.State)delimiter(')>operator(,) string<delimiter(')content(Default)delimiter(')>(\))
+
+ident(states) operator(=) operator([)
+ key(Default)operator(:) local_variable(this)operator(.)operator(&)ident(frontPage)operator(,)
+ key(Shirt)operator(:) local_variable(this)operator(.)operator(&)ident(shirt)operator(,)
+ key(Sweater)operator(:) local_variable(this)operator(.)operator(&)ident(sweater)operator(,)
+ key(Checkout)operator(:) local_variable(this)operator(.)operator(&)ident(checkout)operator(,)
+ key(Card)operator(:) local_variable(this)operator(.)operator(&)ident(creditCard)operator(,)
+ key(Order)operator(:) local_variable(this)operator(.)operator(&)ident(order)operator(,)
+ key(Cancel)operator(:) local_variable(this)operator(.)operator(&)ident(frontPage)operator(,)
+(])
+
+ident(writer) operator(=) keyword(new) pre_type(StringWriter)operator(()(\))
+ident(b) operator(=) keyword(new) ident(MarkupBuilder)operator(()ident(writer)(\))
+ident(b)operator(.)ident(html)operator({)
+ ident(head)operator({) ident(title)operator(()string<delimiter(')content(chemiserie store)delimiter(')>(\)) (})
+ ident(body) operator({)
+ keyword(if) operator(()ident(states)operator([)ident(page)(]\)) ident(process)operator(()ident(page)(\))
+ keyword(else) ident(noSuchPage)operator(()(\))
+ (})
+(})
+ident(println) ident(writer)operator(.)ident(toString)operator(()(\))
+
+keyword(def) method(process)operator(()ident(page)(\)) operator({)
+ ident(b)operator(.)ident(form)operator({)
+ ident(states)operator(.)ident(each)operator({) ident(key)operator(,) ident(closure) operator(->)
+ ident(closure)operator(()ident(page) operator(==) ident(key)(\))
+ (})
+ (})
+(})
+
+keyword(def) method(noSuchPage)operator(()(\)) operator({)
+ ident(b)operator(.)ident(p)operator(()string<delimiter(')content(Unknown request)delimiter(')>(\))
+ ident(reset)operator(()string<delimiter(')content(Click here to start over)delimiter(')>(\))
+(})
+
+keyword(def) method(shopMenu)operator(()(\)) operator({)
+ ident(b)operator(.)ident(p)operator(()(\))
+ ident(toPage)operator(()string<delimiter(")content(Shirt)delimiter(")>(\))
+ ident(toPage)operator(()string<delimiter(")content(Sweater)delimiter(")>(\))
+ ident(toPage)operator(()string<delimiter(")content(Checkout)delimiter(")>(\))
+ ident(reset)operator(()string<delimiter(')content(Empty My Shopping Cart)delimiter(')>(\))
+(})
+
+keyword(def) method(frontPage)operator(()ident(active)(\)) operator({)
+ keyword(if) operator(()operator(!)ident(active)(\)) keyword(return)
+ ident(b)operator(.)ident(h1)operator(()string<delimiter(')content(Hi!)delimiter(')>(\))
+ ident(b)operator(.)ident(p)operator(()string<delimiter(')content(Welcome to our Shirt Shop! Please make your selection from the menu below.)delimiter(')>(\))
+ ident(shopMenu)operator(()(\))
+(})
+
+keyword(def) method(shirt)operator(()ident(active)(\)) operator({)
+ keyword(def) ident(sizes) operator(=) operator([)string<delimiter(')content(XL)delimiter(')>operator(,) string<delimiter(')content(L)delimiter(')>operator(,) string<delimiter(')content(M)delimiter(')>operator(,) string<delimiter(')content(S)delimiter(')>(])
+ keyword(def) ident(colors) operator(=) operator([)string<delimiter(')content(Black)delimiter(')>operator(,) string<delimiter(')content(White)delimiter(')>(])
+ keyword(def) ident(count) operator(=) ident(param)operator(()string<delimiter(')content(shirt_count)delimiter(')>operator(,)integer(0)(\))
+ keyword(def) ident(color) operator(=) ident(param)operator(()string<delimiter(')content(shirt_color)delimiter(')>(\))
+ keyword(def) ident(size) operator(=) ident(param)operator(()string<delimiter(')content(shirt_size)delimiter(')>(\))
+ comment(// sanity check)
+ keyword(if) operator(()ident(count)(\)) operator({)
+ keyword(if) operator(()operator(!)operator(()ident(color) keyword(in) ident(colors)(\)\)) ident(color) operator(=) ident(colors)operator([)integer(0)(])
+ keyword(if) operator(()operator(!)operator(()ident(size) keyword(in) ident(sizes)(\)\)) ident(size) operator(=) ident(sizes)operator([)integer(0)(])
+ (})
+ keyword(if) operator(()operator(!)ident(active)(\)) operator({)
+ keyword(if) operator(()ident(size)(\)) ident(hidden)operator(()string<delimiter(")content(shirt_size)delimiter(")>operator(,) ident(size)(\))
+ keyword(if) operator(()ident(color)(\)) ident(hidden)operator(()string<delimiter(")content(shirt_color)delimiter(")>operator(,) ident(color)(\))
+ keyword(if) operator(()ident(count)(\)) ident(hidden)operator(()string<delimiter(")content(shirt_count)delimiter(")>operator(,) ident(count)(\))
+ keyword(return)
+ (})
+ ident(b)operator(.)ident(h1) string<delimiter(')content(T-Shirt)delimiter(')>
+ ident(b)operator(.)ident(p) string<delimiter(''')content(What a shirt! This baby is decked out with all the options.
+ It comes with full luxury interior, cotton trim, and a collar
+ to make your eyes water! Unit price: )content($)content(33.00)delimiter(''')>
+ ident(b)operator(.)ident(h2) string<delimiter(')content(Options)delimiter(')>
+ ident(label)operator(()string<delimiter(")content(How Many?)delimiter(")>(\))operator(;) ident(textfield)operator(()string<delimiter(")content(shirt_count)delimiter(")>(\))
+ ident(label)operator(()string<delimiter(")content(Size?)delimiter(")>(\))operator(;) ident(dropDown)operator(()string<delimiter(")content(shirt_size)delimiter(")>operator(,) ident(sizes)(\))
+ ident(label)operator(()string<delimiter(")content(Color?)delimiter(")>(\))operator(;) ident(dropDown)operator(()string<delimiter(")content(shirt_color)delimiter(")>operator(,) ident(colors)(\))
+ ident(shopMenu)operator(()(\))
+(})
+
+keyword(def) method(sweater)operator(()ident(active)(\)) operator({)
+ keyword(def) ident(sizes) operator(=) operator([)string<delimiter(')content(XL)delimiter(')>operator(,) string<delimiter(')content(L)delimiter(')>operator(,) string<delimiter(')content(M)delimiter(')>(])
+ keyword(def) ident(colors) operator(=) operator([)string<delimiter(')content(Chartreuse)delimiter(')>operator(,) string<delimiter(')content(Puce)delimiter(')>operator(,) string<delimiter(')content(Lavender)delimiter(')>(])
+ keyword(def) ident(count) operator(=) ident(param)operator(()string<delimiter(')content(sweater_count)delimiter(')>operator(,)integer(0)(\))
+ keyword(def) ident(color) operator(=) ident(param)operator(()string<delimiter(')content(sweater_color)delimiter(')>(\))
+ keyword(def) ident(size) operator(=) ident(param)operator(()string<delimiter(')content(sweater_size)delimiter(')>(\))
+ comment(// sanity check)
+ keyword(if) operator(()ident(count)(\)) operator({)
+ keyword(if) operator(()operator(!)operator(()ident(color) keyword(in) ident(colors)(\)\)) ident(color) operator(=) ident(colors)operator([)integer(0)(])
+ keyword(if) operator(()operator(!)operator(()ident(size) keyword(in) ident(sizes)(\)\)) ident(size) operator(=) ident(sizes)operator([)integer(0)(])
+ (})
+ keyword(if) operator(()operator(!)ident(active)(\)) operator({)
+ keyword(if) operator(()ident(size)(\)) ident(hidden)operator(()string<delimiter(")content(sweater_size)delimiter(")>operator(,) ident(size)(\))
+ keyword(if) operator(()ident(color)(\)) ident(hidden)operator(()string<delimiter(")content(sweater_color)delimiter(")>operator(,) ident(color)(\))
+ keyword(if) operator(()ident(count)(\)) ident(hidden)operator(()string<delimiter(")content(sweater_count)delimiter(")>operator(,) ident(count)(\))
+ keyword(return)
+ (})
+ ident(b)operator(.)ident(h1)operator(()string<delimiter(")content(Sweater)delimiter(")>(\))
+ ident(b)operator(.)ident(p)operator(()string<delimiter(")content(Nothing implies preppy elegance more than this fine )delimiter(")> operator(+)
+ string<delimiter(")content(sweater. Made by peasant workers from black market silk, )delimiter(")> operator(+)
+ string<delimiter(")content(it slides onto your lean form and cries out ``Take me, )delimiter(")> operator(+)
+ string<delimiter(")content(for I am a god!''. Unit price: )char(\\$)content(49.99.)delimiter(")>(\))
+ ident(b)operator(.)ident(h2)operator(()string<delimiter(")content(Options)delimiter(")>(\))
+ ident(label)operator(()string<delimiter(")content(How Many?)delimiter(")>(\))operator(;) ident(textfield)operator(()string<delimiter(")content(sweater_count)delimiter(")>(\))
+ ident(label)operator(()string<delimiter(")content(Size?)delimiter(")>(\))operator(;) ident(dropDown)operator(()string<delimiter(")content(sweater_size)delimiter(")>operator(,) ident(sizes)(\))
+ ident(label)operator(()string<delimiter(")content(Color?)delimiter(")>(\))operator(;) ident(dropDown)operator(()string<delimiter(")content(sweater_color)delimiter(")>operator(,) ident(colors)(\))
+ ident(shopMenu)operator(()(\))
+(})
+
+keyword(def) method(checkout)operator(()ident(active)(\)) operator({)
+ keyword(if) operator(()operator(!)ident(active)(\)) keyword(return)
+ ident(b)operator(.)ident(h1)operator(()string<delimiter(")content(Order Confirmation)delimiter(")>(\))
+ ident(b)operator(.)ident(p)operator(()string<delimiter(")content(You ordered the following:)delimiter(")>(\))
+ ident(orderText)operator(()(\))
+ ident(b)operator(.)ident(p)operator(()string<delimiter(")content(Is this right? Select 'Card' to pay for the items)delimiter(")> operator(+)
+ string<delimiter(")content(or 'Shirt' or 'Sweater' to continue shopping.)delimiter(")>(\))
+ ident(toPage)operator(()string<delimiter(")content(Card)delimiter(")>(\))
+ ident(toPage)operator(()string<delimiter(")content(Shirt)delimiter(")>(\))
+ ident(toPage)operator(()string<delimiter(")content(Sweater)delimiter(")>(\))
+(})
+
+keyword(def) method(creditCard)operator(()ident(active)(\)) operator({)
+ keyword(def) ident(widgets) operator(=) string<delimiter(')content(Name Address1 Address2 City Zip State Phone Card Expiry)delimiter(')>operator(.)ident(split)operator(()string<delimiter(')content( )delimiter(')>(\))
+ keyword(if) operator(()operator(!)ident(active)(\)) operator({)
+ ident(widgets)operator(.)ident(each)operator({) ident(hidden)operator(()local_variable(it)(\)) (})
+ keyword(return)
+ (})
+ ident(b)operator(.)ident(pre)operator({)
+ ident(label)operator(()string<delimiter(")content(Name: )delimiter(")>(\))operator(;) ident(textfield)operator(()string<delimiter(")content(Name)delimiter(")>(\))
+ ident(label)operator(()string<delimiter(")content(Address: )delimiter(")>(\))operator(;) ident(textfield)operator(()string<delimiter(")content(Address1)delimiter(")>(\))
+ ident(label)operator(()string<delimiter(")content( )delimiter(")>(\))operator(;) ident(textfield)operator(()string<delimiter(")content(Address2)delimiter(")>(\))
+ ident(label)operator(()string<delimiter(")content(City: )delimiter(")>(\))operator(;) ident(textfield)operator(()string<delimiter(")content(City)delimiter(")>(\))
+ ident(label)operator(()string<delimiter(")content(Zip: )delimiter(")>(\))operator(;) ident(textfield)operator(()string<delimiter(")content(Zip)delimiter(")>(\))
+ ident(label)operator(()string<delimiter(")content(State: )delimiter(")>(\))operator(;) ident(textfield)operator(()string<delimiter(")content(State)delimiter(")>(\))
+ ident(label)operator(()string<delimiter(")content(Phone: )delimiter(")>(\))operator(;) ident(textfield)operator(()string<delimiter(")content(Phone)delimiter(")>(\))
+ ident(label)operator(()string<delimiter(")content(Credit Card #: )delimiter(")>(\))operator(;) ident(textfield)operator(()string<delimiter(")content(Card)delimiter(")>(\))
+ ident(label)operator(()string<delimiter(")content(Expiry: )delimiter(")>(\))operator(;) ident(textfield)operator(()string<delimiter(")content(Expiry)delimiter(")>(\))
+ (})
+ ident(b)operator(.)ident(p)operator(()string<delimiter(")content(Click on 'Order' to order the items. Click on 'Cancel' to return shopping.)delimiter(")>(\))
+ ident(toPage)operator(()string<delimiter(")content(Order)delimiter(")>(\))
+ ident(toPage)operator(()string<delimiter(")content(Cancel)delimiter(")>(\))
+(})
+
+keyword(def) method(order)operator(()ident(active)(\)) operator({)
+ keyword(if) operator(()operator(!)ident(active)(\)) keyword(return)
+ ident(b)operator(.)ident(h1)operator(()string<delimiter(")content(Ordered!)delimiter(")>(\))
+ ident(b)operator(.)ident(p)operator(()string<delimiter(")content(You have ordered the following items:)delimiter(")>(\))
+ ident(orderText)operator(()(\))
+ ident(reset)operator(()string<delimiter(')content(Begin Again)delimiter(')>(\))
+(})
+
+keyword(def) method(orderText)operator(()(\)) operator({)
+ keyword(def) ident(shirts) operator(=) ident(param)operator(()string<delimiter(')content(shirt_count)delimiter(')>(\))
+ keyword(def) ident(sweaters) operator(=) ident(param)operator(()string<delimiter(')content(sweater_count)delimiter(')>(\))
+ keyword(if) operator(()ident(shirts)(\)) operator({)
+ ident(b)operator(.)ident(p)operator(()string<delimiter(""")content(You have ordered )inline<inline_delimiter(${)ident(param('shirt_count'\))inline_delimiter(})>content(
+ shirts of size )inline<inline_delimiter(${)ident(param('shirt_size'\))inline_delimiter(})>content(
+ and color )inline<inline_delimiter(${)ident(param("shirt_color"\))inline_delimiter(})>content(.)delimiter(""")>(\))
+ (})
+ keyword(if) operator(()ident(sweaters)(\)) operator({)
+ ident(b)operator(.)ident(p)operator(()string<delimiter(""")content(You have ordered )inline<inline_delimiter(${)ident(param('sweater_count'\))inline_delimiter(})>content(
+ sweaters of size )inline<inline_delimiter(${)ident(param('sweater_size'\))inline_delimiter(})>content(
+ and color )inline<inline_delimiter(${)ident(param('sweater_color'\))inline_delimiter(})>content(.)delimiter(""")>(\))
+ (})
+ keyword(if) operator(()operator(!)ident(sweaters) operator(&&) operator(!)ident(shirts)(\)) ident(b)operator(.)ident(p)operator(()string<delimiter(")content(Nothing!)delimiter(")>(\))
+ ident(b)operator(.)ident(p)operator(()string<delimiter(")content(For a total cost of )inline<inline_delimiter(${)ident(calcPrice(\))inline_delimiter(})>delimiter(")>(\))
+(})
+
+keyword(def) method(label)operator(()ident(text)(\)) operator({) ident(b)operator(.)ident(span)operator(()ident(text)(\)) (})
+keyword(def) method(reset)operator(()ident(text)(\)) operator({) ident(b)operator(.)ident(a)operator(()key(href)operator(:)ident(request)operator(.)ident(requestURI)operator(,)ident(text)(\)) (})
+keyword(def) method(toPage)operator(()ident(name)(\)) operator({) ident(b)operator(.)ident(input)operator(()key(type)operator(:)string<delimiter(')content(submit)delimiter(')>operator(,) key(name)operator(:)string<delimiter(')content(.State)delimiter(')>operator(,) key(value)operator(:)ident(name)(\)) (})
+keyword(def) method(dropDown)operator(()ident(name)operator(,) ident(values)(\)) operator({)
+ ident(b)operator(.)ident(select)operator(()key(name)operator(:)ident(name)(\))operator({)
+ ident(values)operator(.)ident(each)operator({)
+ keyword(if) operator(()ident(param)operator(()ident(name)(\))operator(==)local_variable(it)(\)) ident(option)operator(()key(value)operator(:)local_variable(it)operator(,) key(selected)operator(:)keyword(true)operator(,) local_variable(it)(\))
+ keyword(else) ident(option)operator(()key(value)operator(:)local_variable(it)operator(,) local_variable(it)(\))
+ (})
+ (})
+ ident(b)operator(.)ident(br)operator(()(\))
+(})
+keyword(def) method(hidden)operator(()ident(name)(\)) operator({)
+ keyword(if) operator(()ident(binding)operator(.)ident(variables)operator(.)ident(containsKey)operator(()ident(name)(\)\)) ident(v) operator(=) ident(binding)operator([)ident(name)(])
+ keyword(else) ident(v) operator(=) string<delimiter(')delimiter(')>
+ ident(hidden)operator(()ident(name)operator(,) ident(v)(\))
+(})
+keyword(def) method(hidden)operator(()ident(name)operator(,) ident(value)(\)) operator({) ident(b)operator(.)ident(input)operator(()key(type)operator(:)string<delimiter(')content(hidden)delimiter(')>operator(,) key(name)operator(:)ident(name)operator(,) key(value)operator(:)ident(value)(\)) (})
+keyword(def) method(textfield)operator(()ident(name)(\)) operator({) ident(b)operator(.)ident(input)operator(()key(type)operator(:)string<delimiter(')content(text)delimiter(')>operator(,) key(name)operator(:)ident(name)operator(,) key(value)operator(:)ident(param)operator(()ident(name)operator(,)string<delimiter(')delimiter(')>(\)\))operator(;) ident(b)operator(.)ident(br)operator(()(\)) (})
+keyword(def) method(param)operator(()ident(name)(\)) operator({) ident(request)operator(.)ident(getParameter)operator(()ident(name)(\)) (})
+keyword(def) method(param)operator(()ident(name)operator(,) ident(defValue)(\)) operator({)
+ keyword(def) ident(val) operator(=) ident(request)operator(.)ident(getParameter)operator(()ident(name)(\))
+ keyword(if) operator(()ident(val)(\)) keyword(return) ident(val) keyword(else) keyword(return) ident(defValue)
+(})
+
+keyword(def) method(calcPrice)operator(()(\)) operator({)
+ keyword(def) ident(shirts) operator(=) ident(param)operator(()string<delimiter(')content(shirt_count)delimiter(')>operator(,) integer(0)(\))operator(.)ident(toInteger)operator(()(\))
+ keyword(def) ident(sweaters) operator(=) ident(param)operator(()string<delimiter(')content(sweater_count)delimiter(')>operator(,) integer(0)(\))operator(.)ident(toInteger)operator(()(\))
+ keyword(return) operator(()ident(shirts) operator(*) integer(33) operator(+) ident(sweaters) operator(*) float(49.99)(\))operator(.)ident(toString)operator(()(\))
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.0)
+comment(//----------------------------------------------------------------------------------)
+comment(// Many packages are available for simulating a browser. A good starting point:)
+comment(// http://groovy.codehaus.org/Testing+Web+Applications)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.1)
+comment(//----------------------------------------------------------------------------------)
+comment(// for non-binary content)
+ident(urlStr) operator(=) string<delimiter(')content(http://groovy.codehaus.org)delimiter(')>
+ident(content) operator(=) keyword(new) pre_type(URL)operator(()ident(urlStr)(\))operator(.)ident(text)
+ident(println) ident(content)operator(.)ident(size)operator(()(\)) comment(// => 34824)
+
+comment(// for binary content)
+ident(urlStr) operator(=) string<delimiter(')content(http://groovy.codehaus.org/download/attachments/1871/gina_3d.gif)delimiter(')>
+ident(bytes) operator(=) keyword(new) pre_type(ByteArrayOutputStream)operator(()(\))
+ident(bytes) operator(<)operator(<) keyword(new) pre_type(URL)operator(()ident(urlStr)(\))operator(.)ident(openStream)operator(()(\))
+ident(println) ident(bytes)operator(.)ident(size)operator(()(\)) comment(// => 6066)
+
+comment(// various forms of potential error checking)
+keyword(try) operator({)
+ keyword(new) pre_type(URL)operator(()string<delimiter(')content(x:y:z)delimiter(')>(\))
+(}) keyword(catch) operator(()pre_type(MalformedURLException) ident(ex)(\)) operator({)
+ ident(println) ident(ex)operator(.)ident(message) comment(// => unknown protocol: x)
+(})
+keyword(try) operator({)
+ keyword(new) pre_type(URL)operator(()string<delimiter(')content(cnn.com/not.there)delimiter(')>(\))
+(}) keyword(catch) operator(()pre_type(MalformedURLException) ident(ex)(\)) operator({)
+ ident(println) ident(ex)operator(.)ident(message) comment(// => no protocol: cnn.com/not.there)
+(})
+keyword(try) operator({)
+ ident(content) operator(=) keyword(new) pre_type(URL)operator(()string<delimiter(')content(http://cnn.com/not.there)delimiter(')>(\))operator(.)ident(text)
+(}) keyword(catch) operator(()pre_type(FileNotFoundException) ident(ex)(\)) operator({)
+ ident(println) string<delimiter(")content(Couldn't find: )delimiter(")> operator(+) ident(ex)operator(.)ident(message)
+ comment(// => Couldn't find: http://www.cnn.com/not.there)
+(})
+
+comment(// titleBytes example)
+keyword(def) method(titleBytes)operator(()ident(urlStr)(\)) operator({)
+ keyword(def) ident(lineCount) operator(=) integer(0)operator(;) keyword(def) ident(byteCount) operator(=) integer(0)
+ keyword(new) pre_type(URL)operator(()ident(urlStr)(\))operator(.)ident(eachLine)operator({) ident(line) operator(->)
+ ident(lineCount)operator(++)operator(;) ident(byteCount) operator(+=) ident(line)operator(.)ident(size)operator(()(\))
+ (})
+ ident(println) string<delimiter(")inline<inline_delimiter($)ident(urlStr)>content( => ()inline<inline_delimiter($)ident(lineCount)>content( lines, )inline<inline_delimiter($)ident(byteCount)>content( bytes\))delimiter(")>
+(})
+ident(titleBytes)operator(()string<delimiter(')content(http://www.tpj.com/)delimiter(')>(\))
+comment(// http://www.tpj.com/ => (677 lines, 25503 bytes\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.2)
+comment(//----------------------------------------------------------------------------------)
+comment(// using HtmlUnit (htmlunit.sf.net\))
+keyword(import) include(com.gargoylesoftware.htmlunit.WebClient)
+
+keyword(def) ident(webClient) operator(=) keyword(new) ident(WebClient)operator(()(\))
+keyword(def) ident(page) operator(=) ident(webClient)operator(.)ident(getPage)operator(()string<delimiter(')content(http://search.cpan.org/)delimiter(')>(\))
+comment(// check page title)
+keyword(assert) ident(page)operator(.)ident(titleText)operator(.)ident(startsWith)operator(()string<delimiter(')content(The CPAN Search Site)delimiter(')>(\))
+comment(// fill in form and submit it)
+keyword(def) ident(form) operator(=) ident(page)operator(.)ident(getFormByName)operator(()string<delimiter(')content(f)delimiter(')>(\))
+keyword(def) ident(field) operator(=) ident(form)operator(.)ident(getInputByName)operator(()string<delimiter(')content(query)delimiter(')>(\))
+ident(field)operator(.)ident(setValueAttribute)operator(()string<delimiter(')content(DB_File)delimiter(')>(\))
+keyword(def) ident(button) operator(=) ident(form)operator(.)ident(getInputByValue)operator(()string<delimiter(')content(CPAN Search)delimiter(')>(\))
+keyword(def) ident(result) operator(=) ident(button)operator(.)ident(click)operator(()(\))
+comment(// check search result has at least one link ending in DB_File.pm)
+keyword(assert) ident(result)operator(.)ident(anchors)operator(.)ident(any)operator({) ident(a) operator(->) ident(a)operator(.)ident(hrefAttribute)operator(.)ident(endsWith)operator(()string<delimiter(')content(DB_File.pm)delimiter(')>(\)) (})
+
+comment(// fields must be properly escaped)
+ident(println) pre_type(URLEncoder)operator(.)ident(encode)operator(()regexp<delimiter(/)content("this isn't <EASY>&<FUN>")delimiter(/)>operator(,) string<delimiter(')content(utf-8)delimiter(')>(\))
+comment(// => %22this+isn%27t+%3CEASY%3E%26%3CFUN%3E%22)
+
+comment(// proxies can be taken from environment, or specified)
+comment(//System.properties.putAll( ["http.proxyHost":"proxy-host", "http.proxyPort":"proxy-port",)
+comment(// "http.proxyUserName":"user-name", "http.proxyPassword":"proxy-passwd"] \))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.3)
+comment(//----------------------------------------------------------------------------------)
+comment(// using HtmlUnit (htmlunit.sf.net\))
+keyword(import) include(com.gargoylesoftware.htmlunit.WebClient)
+
+ident(client) operator(=) keyword(new) ident(WebClient)operator(()(\))
+ident(html) operator(=) ident(client)operator(.)ident(getPage)operator(()string<delimiter(')content(http://www.perl.com/CPAN/)delimiter(')>(\))
+ident(println) ident(page)operator(.)ident(anchors)operator(.)ident(collect)operator({) local_variable(it)operator(.)ident(hrefAttribute) (})operator(.)ident(sort)operator(()(\))operator(.)ident(unique)operator(()(\))operator(.)ident(join)operator(()string<delimiter(')content(\\n)delimiter(')>(\))
+comment(// =>)
+comment(// disclaimer.html)
+comment(// http://bookmarks.cpan.org/)
+comment(// http://faq.perl.org/)
+comment(// mailto:cpan@perl.org)
+comment(// ...)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.4)
+comment(//----------------------------------------------------------------------------------)
+comment(// split paragraphs)
+ident(LS) operator(=) pre_type(System)operator(.)ident(properties)operator(.)string<delimiter(')content(line.separator)delimiter(')>
+keyword(new) pre_type(File)operator(()ident(args)operator([)integer(0)(]\))operator(.)ident(text)operator(.)ident(split)operator(()string<delimiter(")inline<inline_delimiter($)ident(LS)>inline<inline_delimiter($)ident(LS)>delimiter(")>(\))operator(.)ident(each)operator({) ident(para) operator(->)
+ keyword(if) operator(()ident(para)operator(.)ident(startsWith)operator(()string<delimiter(")content( )delimiter(")>(\)\)) ident(println) string<delimiter(")content(<pre>)char(\\n)inline<inline_delimiter($)ident(para)>char(\\n)content(</pre>)delimiter(")>
+ keyword(else) operator({)
+ ident(para) operator(=) ident(para)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((?m\)^(>.*?\))content($)delimiter(/)>operator(,) regexp<delimiter(/)content($)content(1<br )content(\\/)content(>)delimiter(/)>(\)) comment(// quoted text)
+ ident(para) operator(=) ident(para)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(<URL:(.*\)>)delimiter(/)>operator(,) regexp<delimiter(/)content(<a href=")content($)content(1">)content($)content(1<)content(\\/)content(a>)delimiter(/)>(\)) comment(// embedded URL)
+ ident(para) operator(=) ident(para)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((http:)char(\\S)content(+\))delimiter(/)>operator(,) regexp<delimiter(/)content(<a href=")content($)content(1">)content($)content(1<)content(\\/)content(a>)delimiter(/)>(\)) comment(// guessed URL)
+ ident(para) operator(=) ident(para)operator(.)ident(replaceAll)operator(()string<delimiter(')char(\\\\)content(*()char(\\\\)content(S+\))char(\\\\)content(*)delimiter(')>operator(,) regexp<delimiter(/)content(<strong>)content($)content(1<)content(\\/)content(strong>)delimiter(/)>(\)) comment(// this is *bold* here)
+ ident(para) operator(=) ident(para)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)char(\\b)content(_()char(\\S)content(+\)_)char(\\b)delimiter(/)>operator(,) regexp<delimiter(/)content(<em>)content($)content(1<)content(\\/)content(em>)delimiter(/)>(\)) comment(// this is _italic_ here)
+ ident(println) string<delimiter(")content(<p>)char(\\n)inline<inline_delimiter($)ident(para)>char(\\n)content(</p>)delimiter(")> comment(// add paragraph tags)
+ (})
+(})
+
+keyword(def) method(encodeEmail)operator(()ident(email)(\)) operator({)
+ ident(println) string<delimiter(")content(<table>)delimiter(")>
+ ident(email) operator(=) pre_type(URLEncoder)operator(.)ident(encode)operator(()ident(email)(\))
+ ident(email) operator(=) ident(text)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content(()char(\\n)content([ )char(\\t)content(]+\))delimiter(/)>operator(,) operator(/) operator(.) regexp<delimiter(/)content(\) )delimiter(/)>operator(/) ident(continuation) ident(lines)
+ ident(email) operator(=) ident(text)operator(.)ident(replaceAll)operator(()regexp<delimiter(/)content((?m\)^()char(\\S)content(+?:\))char(\\s)content(*(.*?\))content($)delimiter(/)>operator(,)
+ regexp<delimiter(/)content(<tr><th align="left">)content($)content(1<)content(\\/)content(th><td>)content($)content(2<)content(\\/)content(td><)content(\\/)content(tr>)delimiter(/)>(\))operator(;)
+ ident(println) ident(email)
+ ident(println) string<delimiter(")content(</table>)delimiter(")>
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.5)
+comment(//----------------------------------------------------------------------------------)
+comment(// using CyberNeko Parser (people.apache.org/~andyc/neko/doc\))
+ident(parser) operator(=) keyword(new) ident(org)operator(.)ident(cyberneko)operator(.)ident(html)operator(.)ident(parsers)operator(.)ident(SAXParser)operator(()(\))
+ident(parser)operator(.)ident(setFeature)operator(()string<delimiter(')content(http://xml.org/sax/features/namespaces)delimiter(')>operator(,) keyword(false)(\))
+ident(page) operator(=) keyword(new) ident(XmlParser)operator(()ident(parser)(\))operator(.)ident(parse)operator(()string<delimiter(')content(http://www.perl.com/CPAN/)delimiter(')>(\))
+ident(page)operator(.)ident(depthFirst)operator(()(\))operator(.)ident(each)operator({) ident(println) local_variable(it)operator(.)ident(text)operator(()(\)) (})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.6)
+comment(//----------------------------------------------------------------------------------)
+comment(// removing tags, see 20.5)
+
+comment(// extracting tags: htitle using cyberneko and XmlSlurper)
+ident(parser) operator(=) keyword(new) ident(org)operator(.)ident(cyberneko)operator(.)ident(html)operator(.)ident(parsers)operator(.)ident(SAXParser)operator(()(\))
+ident(parser)operator(.)ident(setFeature)operator(()string<delimiter(')content(http://xml.org/sax/features/namespaces)delimiter(')>operator(,) keyword(false)(\))
+ident(page) operator(=) keyword(new) ident(XmlParser)operator(()ident(parser)(\))operator(.)ident(parse)operator(()string<delimiter(')content(http://www.perl.com/CPAN/)delimiter(')>(\))
+ident(println) ident(page)operator(.)ident(HEAD)operator(.)ident(TITLE)operator([)integer(0)(])operator(.)ident(text)operator(()(\))
+
+comment(// extracting tags: htitle using HtmlUnit)
+ident(client) operator(=) keyword(new) ident(WebClient)operator(()(\))
+ident(html) operator(=) ident(client)operator(.)ident(getPage)operator(()string<delimiter(')content(http://www.perl.com/CPAN/)delimiter(')>(\))
+ident(println) ident(html)operator(.)ident(titleText)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.7)
+comment(//----------------------------------------------------------------------------------)
+keyword(import) include(com.gargoylesoftware.htmlunit.WebClient)
+
+ident(client) operator(=) keyword(new) ident(WebClient)operator(()(\))
+ident(page) operator(=) ident(client)operator(.)ident(getPage)operator(()string<delimiter(')content(http://www.perl.com/CPAN/)delimiter(')>(\))
+ident(page)operator(.)ident(anchors)operator(.)ident(each)operator({)
+ ident(checkUrl)operator(()ident(page)operator(,) local_variable(it)operator(.)ident(hrefAttribute)(\))
+(})
+
+keyword(def) method(checkUrl)operator(()ident(page)operator(,) ident(url)(\)) operator({)
+ keyword(try) operator({)
+ ident(print) string<delimiter(")inline<inline_delimiter($)ident(url)>content( )delimiter(")>
+ ident(qurl) operator(=) ident(page)operator(.)ident(getFullyQualifiedUrl)operator(()ident(url)(\))
+ ident(client)operator(.)ident(getPage)operator(()ident(qurl)(\))
+ ident(println) string<delimiter(')content(OK)delimiter(')>
+ (}) keyword(catch) operator(()pre_type(Exception) ident(ex)(\)) operator({)
+ ident(println) string<delimiter(')content(BAD)delimiter(')>
+ (})
+(})
+comment(// =>)
+comment(// modules/index.html OK)
+comment(// RECENT.html OK)
+comment(// http://search.cpan.org/recent OK)
+comment(// http://mirrors.cpan.org/ OK)
+comment(// http://perldoc.perl.org/ OK)
+comment(// mailto:cpan@perl.org BAD)
+comment(// http://www.csc.fi/suomi/funet/verkko.html.en/ BAD)
+comment(// ...)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.8)
+comment(//----------------------------------------------------------------------------------)
+keyword(import) include(org.apache.commons.httpclient.HttpClient)
+keyword(import) include(org.apache.commons.httpclient.methods.HeadMethod)
+keyword(import) include(java.text.DateFormat)
+
+ident(urls) operator(=) operator([)
+ string<delimiter(")content(http://www.apache.org/)delimiter(")>operator(,)
+ string<delimiter(")content(http://www.perl.org/)delimiter(")>operator(,)
+ string<delimiter(")content(http://www.python.org/)delimiter(")>operator(,)
+ string<delimiter(")content(http://www.ora.com/)delimiter(")>operator(,)
+ string<delimiter(")content(http://jakarta.apache.org/)delimiter(")>operator(,)
+ string<delimiter(")content(http://www.w3.org/)delimiter(")>
+(])
+
+ident(df) operator(=) pre_type(DateFormat)operator(.)ident(getDateTimeInstance)operator(()pre_type(DateFormat)operator(.)ident(FULL)operator(,) pre_type(DateFormat)operator(.)ident(MEDIUM)(\))
+ident(client) operator(=) keyword(new) ident(HttpClient)operator(()(\))
+ident(urlInfo) operator(=) operator([)operator(:)(])
+ident(urls)operator(.)ident(each)operator({) ident(url) operator(->)
+ ident(head) operator(=) keyword(new) ident(HeadMethod)operator(()ident(url)(\))
+ ident(client)operator(.)ident(executeMethod)operator(()ident(head)(\))
+ ident(lastModified) operator(=) ident(head)operator(.)ident(getResponseHeader)operator(()string<delimiter(")content(last-modified)delimiter(")>(\))operator(?)operator(.)ident(value)
+ ident(urlInfo)operator([)ident(df)operator(.)ident(parse)operator(()ident(lastModified)(\)])operator(=)ident(url)
+(})
+
+ident(urlInfo)operator(.)ident(keySet)operator(()(\))operator(.)ident(sort)operator(()(\))operator(.)ident(each)operator({) ident(key) operator(->)
+ ident(println) string<delimiter(")inline<inline_delimiter($)ident(key)>content( )inline<inline_delimiter(${)ident(urlInfo[key])inline_delimiter(})>delimiter(")>
+(})
+comment(// =>)
+comment(// Sun Jan 07 21:48:15 EST 2007 http://www.apache.org/)
+comment(// Sat Jan 13 12:44:32 EST 2007 http://jakarta.apache.org/)
+comment(// Fri Jan 19 14:50:13 EST 2007 http://www.w3.org/)
+comment(// Fri Jan 19 19:28:35 EST 2007 http://www.python.org/)
+comment(// Sat Jan 20 09:36:08 EST 2007 http://www.ora.com/)
+comment(// Sat Jan 20 13:25:53 EST 2007 http://www.perl.org/)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.9)
+comment(//----------------------------------------------------------------------------------)
+comment(// GString version (variables must be predefined\):)
+ident(username) operator(=) string<delimiter(')content(Tom)delimiter(')>
+ident(count) operator(=) integer(99)
+ident(total) operator(=) integer(999)
+ident(htmlStr) operator(=) string<delimiter(""")content(
+<!-- simple.template for internal template(\) function -->
+<HTML><HEAD><TITLE>Report for )inline<inline_delimiter($)ident(username)>content(</TITLE></HEAD>
+<BODY><H1>Report for )inline<inline_delimiter($)ident(username)>content(</H1>
+)inline<inline_delimiter($)ident(username)>content( logged in )inline<inline_delimiter($)ident(count)>content( times, for a total of )inline<inline_delimiter($)ident(total)>content( minutes.
+)delimiter(""")>
+ident(println) ident(htmlStr)
+
+comment(// SimpleTemplateEngine version:)
+keyword(def) ident(html) operator(=) string<delimiter(''')content(
+<!-- simple.template for internal template(\) function -->
+<HTML><HEAD><TITLE>Report for )inline<inline_delimiter($)ident(username)>content(</TITLE></HEAD>
+<BODY><H1>Report for )inline<inline_delimiter($)ident(username)>content(</H1>
+)inline<inline_delimiter($)ident(username)>content( logged in )inline<inline_delimiter($)ident(count)>content( times, for a total of )inline<inline_delimiter($)ident(total)>content( minutes.
+)delimiter(''')>
+
+keyword(def) ident(engine) operator(=) keyword(new) ident(groovy)operator(.)ident(text)operator(.)ident(SimpleTemplateEngine)operator(()(\))
+keyword(def) ident(reader) operator(=) keyword(new) pre_type(StringReader)operator(()ident(html)(\))
+keyword(def) ident(template) operator(=) ident(engine)operator(.)ident(createTemplate)operator(()ident(reader)(\))
+ident(println) ident(template)operator(.)ident(make)operator(()key(username)operator(:)string<delimiter(")content(Peter)delimiter(")>operator(,) key(count)operator(:)string<delimiter(")content(23)delimiter(")>operator(,) key(total)operator(:) string<delimiter(")content(1234)delimiter(")>(\))
+
+comment(// SQL version)
+keyword(import) include(groovy.sql.Sql)
+ident(user) operator(=) string<delimiter(')content(Peter)delimiter(')>
+keyword(def) ident(sql) operator(=) ident(Sql)operator(.)ident(newInstance)operator(()string<delimiter(')content(jdbc:mysql://localhost:3306/mydb)delimiter(')>operator(,) string<delimiter(')content(dbuser)delimiter(')>operator(,)
+ string<delimiter(')content(dbpass)delimiter(')>operator(,) string<delimiter(')content(com.mysql.jdbc.Driver)delimiter(')>(\))
+ident(sql)operator(.)ident(query)operator(()string<delimiter(")content(SELECT COUNT(duration\),SUM(duration\) FROM logins WHERE username=')inline<inline_delimiter($)ident(user)>content(')delimiter(")>(\)) operator({) ident(answer) operator(->)
+ ident(println) operator(()ident(template)operator(.)ident(make)operator(()key(username)operator(:)ident(user)operator(,) key(count)operator(:)ident(answer)operator([)integer(0)(])operator(,) key(total)operator(:)ident(answer)operator([)integer(1)(]\)\))
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.10)
+comment(//----------------------------------------------------------------------------------)
+comment(// using built-in connection features)
+ident(urlStr) operator(=) string<delimiter(')content(http://jakarta.apache.org/)delimiter(')>
+ident(url) operator(=) keyword(new) pre_type(URL)operator(()ident(urlStr)(\))
+ident(connection) operator(=) ident(url)operator(.)ident(openConnection)operator(()(\))
+ident(connection)operator(.)ident(ifModifiedSince) operator(=) keyword(new) pre_type(Date)operator(()integer(2007)operator(,)integer(1)operator(,)integer(18)(\))operator(.)ident(time)
+ident(connection)operator(.)ident(connect)operator(()(\))
+ident(println) ident(connection)operator(.)ident(responseCode)
+
+comment(// manually setting header field)
+ident(connection) operator(=) ident(url)operator(.)ident(openConnection)operator(()(\))
+ident(df) operator(=) keyword(new) ident(java)operator(.)ident(text)operator(.)ident(SimpleDateFormat) operator(()string<delimiter(")content(EEE, dd MMM yyyy HH:mm:ss 'GMT')delimiter(")>(\))
+ident(df)operator(.)ident(setTimeZone)operator(()pre_type(TimeZone)operator(.)ident(getTimeZone)operator(()string<delimiter(')content(GMT)delimiter(')>(\)\))
+ident(connection)operator(.)ident(setRequestProperty)operator(()string<delimiter(")content(If-Modified-Since)delimiter(")>operator(,)ident(df)operator(.)ident(format)operator(()keyword(new) pre_type(Date)operator(()integer(2007)operator(,)integer(1)operator(,)integer(18)(\)\)\))operator(;)
+ident(connection)operator(.)ident(connect)operator(()(\))
+ident(println) ident(connection)operator(.)ident(responseCode)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.11)
+comment(//----------------------------------------------------------------------------------)
+comment(// The website http://www.robotstxt.org/wc/active/html/ lists many available robots)
+comment(// including Java ones which can be used from Groovy. In particular, j-spider)
+comment(// allows you to:)
+comment(// + Check your site for errors (internal server errors, ...\))
+comment(// + Outgoing and/or internal link checking)
+comment(// + Analyze your site structure (creating a sitemap, ...\))
+comment(// + Download complete web sites)
+comment(// most of its functionality is available by tweaking appropriate configuration)
+comment(// files and then running it as a standalone application but you can also write)
+comment(// your own java classes.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.12)
+comment(//----------------------------------------------------------------------------------)
+comment(// sample data, use 'LOGFILE = new File(args[0]\).text' or similar)
+ident(LOGFILE) operator(=) string<delimiter(''')content(
+127.0.0.1 - - [04/Sep/2005:20:50:31 +0200] "GET /bus HTTP/1.1" 301 303
+127.0.0.1 - - [04/Sep/2005:20:50:31 +0200] "GET /bus HTTP/1.1" 301 303 "-" "Opera/8.02 (X11; Linux i686; U; en\)"
+192.168.0.1 - - [04/Sep/2005:20:50:36 +0200] "GET /bus/libjs/layersmenu-library.js HTTP/1.1" 200 6228
+192.168.0.1 - - [04/Sep/2005:20:50:36 +0200] "GET /bus/libjs/layersmenu-library.js HTTP/1.1" 200 6228 "http://localhost/bus/" "Opera/8.02 (X11; Linux i686; U; en\)"
+)delimiter(''')>
+
+comment(// similar to perl version:)
+ident(fields) operator(=) operator([)string<delimiter(')content(client)delimiter(')>operator(,)string<delimiter(')content(identuser)delimiter(')>operator(,)string<delimiter(')content(authuser)delimiter(')>operator(,)string<delimiter(')content(date)delimiter(')>operator(,)string<delimiter(')content(time)delimiter(')>operator(,)string<delimiter(')content(tz)delimiter(')>operator(,)string<delimiter(')content(method)delimiter(')>operator(,)string<delimiter(')content(url)delimiter(')>operator(,)string<delimiter(')content(protocol)delimiter(')>operator(,)string<delimiter(')content(status)delimiter(')>operator(,)string<delimiter(')content(bytes)delimiter(')>(])
+ident(regex) operator(=) regexp<delimiter(/)content(^()char(\\S)content(+\) ()char(\\S)content(+\) ()char(\\S)content(+\) )content(\\[)content(([^:]+\):()char(\\d)content(+:)char(\\d)content(+:)char(\\d)content(+\) ([^)content(\\])content(]+\))content(\\])content( "()char(\\S)content(+\) (.*?\) ()char(\\S)content(+\)" ()char(\\S)content(+\) ()char(\\S)content(+\).*)content($)delimiter(/)>
+
+ident(LOGFILE)operator(.)ident(trim)operator(()(\))operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(each)operator({) ident(line) operator(->)
+ ident(m) operator(=) ident(line) operator(=~) ident(regex)
+ keyword(if) operator(()ident(m)operator(.)ident(matches)operator(()(\)\)) operator({)
+ keyword(for) operator(()ident(idx) keyword(in) integer(0)operator(..<)ident(fields)operator(.)ident(size)operator(()(\)\)) operator({) ident(println) string<delimiter(")inline<inline_delimiter(${)ident(fields[idx])inline_delimiter(})>content(=)inline<inline_delimiter(${)ident(m[0][idx+1])inline_delimiter(})>delimiter(")> (})
+ ident(println)operator(()(\))
+ (})
+(})
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.13)
+comment(//----------------------------------------------------------------------------------)
+comment(// sample data, use 'LOGFILE = new File(args[0]\).text' or similar)
+ident(LOGFILE) operator(=) string<delimiter(''')content(
+204.31.113.138 - - [03/Jul/1996:06:56:12 -0800] "POST /forms/login.jsp HTTP/1.0" 200 5593
+fcrawler.looksmart.com - - [26/Apr/2000:00:00:12 -0400] "GET /contacts.html HTTP/1.0" 200 4595 "-" "FAST-WebCrawler/2.1-pre2 (ashen@looksmart.net\)"
+fcrawler.looksmart.com - - [26/Apr/2000:00:17:19 -0400] "GET /news/news.html HTTP/1.0" 200 16716 "-" "FAST-WebCrawler/2.1-pre2 (ashen@looksmart.net\)"
+ppp931.on.bellglobal.com - - [26/Apr/2000:00:16:12 -0400] "GET /download/windows/asctab31.zip HTTP/1.0" 200 1540096 "http://www.htmlgoodies.com/downloads/freeware/webdevelopment/15.html" "Mozilla/4.7 [en]C-SYMPA (Win95; U\)"
+123.123.123.123 - - [26/Apr/2000:00:23:48 -0400] "GET /pics/wpaper.gif HTTP/1.0" 200 6248 "http://www.jafsoft.com/asctortf/" "Mozilla/4.05 (Macintosh; I; PPC\)"
+123.123.123.123 - - [26/Apr/2000:00:23:47 -0400] "GET /asctortf/ HTTP/1.0" 200 8130 "http://search.netscape.com/Computers/Data_Formats/Document/Text/RTF" "Mozilla/4.05 (Macintosh; I; PPC\)"
+123.123.123.123 - - [26/Apr/2000:00:23:48 -0400] "GET /pics/5star2000.gif HTTP/1.0" 200 4005 "http://www.jafsoft.com/asctortf/" "Mozilla/4.05 (Macintosh; I; PPC\)"
+123.123.123.123 - - [27/Apr/2000:00:23:50 -0400] "GET /pics/5star.gif HTTP/1.0" 200 1031 "http://www.jafsoft.com/asctortf/" "Mozilla/4.05 (Macintosh; I; PPC\)"
+123.123.123.123 - - [27/Apr/2000:00:23:51 -0400] "GET /pics/a2hlogo.jpg HTTP/1.0" 200 4282 "http://www.jafsoft.com/asctortf/" "Mozilla/4.05 (Macintosh; I; PPC\)"
+123.123.123.123 - - [27/Apr/2000:00:23:51 -0400] "GET /cgi-bin/newcount?jafsof3&width=4&font=digital&noshow HTTP/1.0" 200 36 "http://www.jafsoft.com/asctortf/" "Mozilla/4.05 (Macintosh; I; PPC\)"
+127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
+127.0.0.1 - - [04/Sep/2005:20:50:31 +0200] "GET / HTTP/1.1" 200 1927
+127.0.0.1 - - [04/Sep/2005:20:50:31 +0200] "GET /bus HTTP/1.1" 301 303 "-" "Opera/8.02 (X11; Linux i686; U; en\)"
+192.168.0.1 - - [05/Sep/2005:20:50:36 +0200] "GET /bus/libjs/layersmenu-library.js HTTP/1.1" 200 6228
+192.168.0.1 - - [05/Sep/2005:20:50:36 +0200] "GET /bus/libjs/layersmenu-library.js HTTP/1.1" 200 6228 "http://localhost/bus/" "Opera/8.02 (X11; Linux i686; U; en\)"
+)delimiter(''')>
+
+ident(fields) operator(=) operator([)string<delimiter(')content(client)delimiter(')>operator(,)string<delimiter(')content(identuser)delimiter(')>operator(,)string<delimiter(')content(authuser)delimiter(')>operator(,)string<delimiter(')content(date)delimiter(')>operator(,)string<delimiter(')content(time)delimiter(')>operator(,)string<delimiter(')content(tz)delimiter(')>operator(,)string<delimiter(')content(method)delimiter(')>operator(,)string<delimiter(')content(url)delimiter(')>operator(,)string<delimiter(')content(protocol)delimiter(')>operator(,)string<delimiter(')content(status)delimiter(')>operator(,)string<delimiter(')content(bytes)delimiter(')>(])
+ident(regex) operator(=) regexp<delimiter(/)content(^()char(\\S)content(+\) ()char(\\S)content(+\) ()char(\\S)content(+\) )content(\\[)content(([^:]+\):()char(\\d)content(+:)char(\\d)content(+:)char(\\d)content(+\) ([^)content(\\])content(]+\))content(\\])content( "()char(\\S)content(+\) (.*?\) ()char(\\S)content(+\)" ()char(\\S)content(+\) ()char(\\S)content(+\).*)content($)delimiter(/)>
+
+type(class) class(Summary) operator({)
+ keyword(def) ident(hosts) operator(=) operator([)operator(:)(])
+ keyword(def) ident(what) operator(=) operator([)operator(:)(])
+ keyword(def) ident(accessCount) operator(=) integer(0)
+ keyword(def) ident(postCount) operator(=) integer(0)
+ keyword(def) ident(homeCount) operator(=) integer(0)
+ keyword(def) ident(totalBytes) operator(=) integer(0)
+(})
+ident(totals) operator(=) operator([)operator(:)(])
+ident(LOGFILE)operator(.)ident(trim)operator(()(\))operator(.)ident(split)operator(()string<delimiter(')content(\\n)delimiter(')>(\))operator(.)ident(each)operator({) ident(line) operator(->)
+ ident(m) operator(=) ident(line) operator(=~) ident(regex)
+ keyword(if) operator(()ident(m)operator(.)ident(matches)operator(()(\)\)) operator({)
+ ident(date) operator(=) ident(m)operator([)integer(0)(])operator([)ident(fields)operator(.)ident(indexOf)operator(()string<delimiter(')content(date)delimiter(')>(\))operator(+)integer(1)(])
+ ident(s) operator(=) ident(totals)operator(.)ident(get)operator(()ident(date)operator(,) keyword(new) ident(Summary)operator(()(\)\))
+ ident(s)operator(.)ident(accessCount)operator(++)
+ keyword(if) operator(()ident(m)operator([)integer(0)(])operator([)ident(fields)operator(.)ident(indexOf)operator(()string<delimiter(')content(method)delimiter(')>(\))operator(+)integer(1)(]) operator(==) string<delimiter(')content(POST)delimiter(')>(\)) ident(s)operator(.)ident(postCount)operator(++)
+ ident(s)operator(.)ident(totalBytes) operator(+=) operator(()ident(m)operator([)integer(0)(])operator([)ident(fields)operator(.)ident(indexOf)operator(()string<delimiter(')content(bytes)delimiter(')>(\))operator(+)integer(1)(]\))operator(.)ident(toInteger)operator(()(\))
+ keyword(def) ident(url) operator(=) ident(m)operator([)integer(0)(])operator([)ident(fields)operator(.)ident(indexOf)operator(()string<delimiter(')content(url)delimiter(')>(\))operator(+)integer(1)(])
+ keyword(if) operator(()ident(url) operator(==) string<delimiter(')content(/)delimiter(')>(\)) ident(s)operator(.)ident(homeCount)operator(++)
+ ident(s)operator(.)ident(what)operator([)ident(url)(]) operator(=) ident(s)operator(.)ident(what)operator(.)ident(get)operator(()ident(url)operator(,) integer(0)(\)) operator(+) integer(1)
+ keyword(def) ident(host) operator(=) ident(m)operator([)integer(0)(])operator([)ident(fields)operator(.)ident(indexOf)operator(()string<delimiter(')content(client)delimiter(')>(\))operator(+)integer(1)(])
+ ident(s)operator(.)ident(hosts)operator([)ident(host)(]) operator(=) ident(s)operator(.)ident(hosts)operator(.)ident(get)operator(()ident(host)operator(,) integer(0)(\)) operator(+) integer(1)
+ (})
+(})
+ident(report)operator(()string<delimiter(')content(Date)delimiter(')>operator(,)string<delimiter(')content(Hosts)delimiter(')>operator(,)string<delimiter(')content(Accesses)delimiter(')>operator(,)string<delimiter(')content(Unidocs)delimiter(')>operator(,)string<delimiter(')content(POST)delimiter(')>operator(,)string<delimiter(')content(Home)delimiter(')>operator(,)string<delimiter(')content(Bytes)delimiter(')>(\))
+ident(totals)operator(.)ident(each)operator({) ident(key)operator(,) ident(s) operator(->)
+ ident(report)operator(()ident(key)operator(,) ident(s)operator(.)ident(hosts)operator(.)ident(size)operator(()(\))operator(,) ident(s)operator(.)ident(accessCount)operator(,) ident(s)operator(.)ident(what)operator(.)ident(size)operator(()(\))operator(,) ident(s)operator(.)ident(postCount)operator(,) ident(s)operator(.)ident(homeCount)operator(,) ident(s)operator(.)ident(totalBytes)(\))
+(})
+ident(v) operator(=) ident(totals)operator(.)ident(values)operator(()(\))
+ident(report)operator(()string<delimiter(')content(Grand Total)delimiter(')>operator(,) ident(v)operator(.)ident(sum)operator({)local_variable(it)operator(.)ident(hosts)operator(.)ident(size)operator(()(\)})operator(,) ident(v)operator(.)ident(sum)operator({)local_variable(it)operator(.)ident(accessCount)(})operator(,) ident(v)operator(.)ident(sum)operator({)local_variable(it)operator(.)ident(what)operator(.)ident(size)operator(()(\)})operator(,)
+ ident(v)operator(.)ident(sum)operator({)local_variable(it)operator(.)ident(postCount)(})operator(,) ident(v)operator(.)ident(sum)operator({)local_variable(it)operator(.)ident(homeCount)(})operator(,) ident(v)operator(.)ident(sum)operator({)local_variable(it)operator(.)ident(totalBytes)(}) (\))
+
+keyword(def) method(report)operator(()ident(a)operator(,) ident(b)operator(,) ident(c)operator(,) ident(d)operator(,) ident(e)operator(,) ident(f)operator(,) ident(g)(\)) operator({)
+ ident(printf) operator(()string<delimiter(")content(%12s %6s %8s %8s %8s %8s %10s)char(\\n)delimiter(")>operator(,) operator([)ident(a)operator(,)ident(b)operator(,)ident(c)operator(,)ident(d)operator(,)ident(e)operator(,)ident(f)operator(,)ident(g)(]\))
+(})
+comment(// =>)
+comment(// Date Hosts Accesses Unidocs POST Home Bytes)
+comment(// 03/Jul/1996 1 1 1 1 0 5593)
+comment(// 10/Oct/2000 1 1 1 0 0 2326)
+comment(// 04/Sep/2005 1 2 2 0 1 2230)
+comment(// 05/Sep/2005 1 2 1 0 0 12456)
+comment(// 26/Apr/2000 3 6 6 0 0 1579790)
+comment(// 27/Apr/2000 1 3 3 0 0 5349)
+comment(// Grand Total 8 15 14 1 1 1607744)
+
+
+comment(// Some open source log processing packages in Java:)
+comment(// http://www.generationjava.com/projects/logview/index.shtml)
+comment(// http://ostermiller.org/webalizer/)
+comment(// http://jxla.nvdcms.org/en/index.xml)
+comment(// http://polliwog.sourceforge.net/index.html)
+comment(// as well as textual reports, most of these can produce graphical reports)
+comment(// Most have their own configuration information and Java extension points.)
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.14)
+comment(//----------------------------------------------------------------------------------)
+ keyword(import) include(org.cyberneko.html.filters.Writer)
+ keyword(import) include(org.cyberneko.html.filters.DefaultFilter)
+ keyword(import) include(org.apache.xerces.xni.parser.XMLDocumentFilter)
+ keyword(import) include(org.apache.xerces.xni.*)
+ keyword(import) include(org.cyberneko.html.parsers.DOMParser)
+ keyword(import) include(org.xml.sax.InputSource)
+
+ ident(input) operator(=) string<delimiter(''')content(
+ <HTML><HEAD><TITLE>Hi!</TITLE></HEAD><BODY>
+ <H1>Welcome to Scooby World!</H1>
+ I have <A HREF="pictures.html">pictures</A> of the crazy dog
+ himself. Here's one!<P>
+ <IMG SRC="scooby.jpg" ALT="Good doggy!"><P>
+ <BLINK>He's my hero!</BLINK> I would like to meet him some day,
+ and get my picture taken with him.<P>
+ P.S. I am deathly ill. <A HREF="shergold.html">Please send
+ cards</A>.
+ </BODY></HTML>
+ )delimiter(''')>
+
+ type(class) class(WordReplaceFilter) directive(extends) ident(DefaultFilter) operator({)
+ directive(private) ident(before)operator(,) ident(after)
+ ident(WordReplaceFilter)operator(()ident(b)operator(,) ident(a)(\)) operator({) ident(before) operator(=) ident(b)operator(;) ident(after) operator(=) ident(a) (})
+ type(void) ident(characters)operator(()ident(XMLString) ident(text)operator(,) ident(Augmentations) ident(augs)(\)) operator({)
+ type(char)type([]) ident(c) operator(=) ident(text)operator(.)ident(toString)operator(()(\))operator(.)ident(replaceAll)operator(()ident(before)operator(,) ident(after)(\))
+ local_variable(super)operator(.)ident(characters)operator(()keyword(new) ident(XMLString)operator(()ident(c)operator(,) integer(0)operator(,) ident(c)operator(.)ident(size)operator(()(\)\))operator(,) ident(augs)(\))
+ (})
+ type(void) ident(setProperty)operator(()pre_type(String) ident(s)operator(,) pre_type(Object) ident(o)(\))operator({)(})
+ (})
+ ident(XMLDocumentFilter)type([]) ident(filters) operator(=) operator([)
+ keyword(new) ident(WordReplaceFilter)operator(()regexp<delimiter(/)content((?sm\)picture)delimiter(/)>operator(,) regexp<delimiter(/)content(photo)delimiter(/)>(\))operator(,)
+ keyword(new) pre_type(Writer)operator(()(\))
+ (])
+ ident(parser) operator(=) keyword(new) ident(DOMParser)operator(()(\))
+ ident(parser)operator(.)ident(setProperty)operator(()string<delimiter(")content(http://cyberneko.org/html/properties/filters)delimiter(")>operator(,) ident(filters)(\))
+ ident(parser)operator(.)ident(parse)operator(()keyword(new) ident(InputSource)operator(()keyword(new) pre_type(StringReader)operator(()ident(input)(\)\)\))
+comment(//----------------------------------------------------------------------------------)
+
+
+comment(// @@PLEAC@@_20.15)
+comment(//----------------------------------------------------------------------------------)
+keyword(import) include(org.cyberneko.html.filters.Writer)
+keyword(import) include(org.cyberneko.html.filters.DefaultFilter)
+keyword(import) include(org.apache.xerces.xni.parser.XMLDocumentFilter)
+keyword(import) include(org.apache.xerces.xni.*)
+keyword(import) include(org.cyberneko.html.parsers.DOMParser)
+keyword(import) include(org.xml.sax.InputSource)
+
+ident(input) operator(=) string<delimiter(''')content(
+<HTML><HEAD><TITLE>Hi!</TITLE></HEAD><BODY>
+<H1>Welcome to Scooby World!</H1>
+I have <A HREF="pictures.html">pictures</A> of the crazy dog
+himself. Here's one!<P>
+<IMG SRC="scooby.jpg" ALT="Good doggy!"><P>
+<BLINK>He's my hero!</BLINK> I would like to meet him some day,
+and get my picture taken with him.<P>
+P.S. I am deathly ill. <A HREF="shergold.html">Please send
+cards</A>.
+</BODY></HTML>
+)delimiter(''')>
+
+type(class) class(HrefReplaceFilter) directive(extends) ident(DefaultFilter) operator({)
+ directive(private) ident(before)operator(,) ident(after)
+ ident(HrefReplaceFilter)operator(()ident(b)operator(,) ident(a)(\)) operator({) ident(before) operator(=) ident(b)operator(;) ident(after) operator(=) ident(a) (})
+ type(void) ident(startElement)operator(()pre_type(QName) ident(element)operator(,) ident(XMLAttributes) ident(attributes)operator(,) ident(Augmentations) ident(augs)(\)) operator({)
+ keyword(def) ident(idx) operator(=) ident(attributes)operator(.)ident(getIndex)operator(()string<delimiter(')content(href)delimiter(')>(\))
+ keyword(if) operator(()ident(idx) operator(!=) operator(-)integer(1)(\)) operator({)
+ keyword(def) ident(newtext) operator(=) ident(attributes)operator(.)ident(getValue)operator(()ident(idx)(\))operator(.)ident(replaceAll)operator(()ident(before)operator(,) ident(after)(\))
+ ident(attributes)operator(.)ident(setValue)operator(()ident(idx)operator(,) pre_type(URLEncoder)operator(.)ident(encode)operator(()ident(newtext)(\)\))
+ (})
+ local_variable(super)operator(.)ident(startElement)operator(()ident(element)operator(,) ident(attributes)operator(,) ident(augs)(\))
+ (})
+ type(void) ident(setProperty)operator(()pre_type(String) ident(s)operator(,) pre_type(Object) ident(o)(\))operator({)(})
+(})
+ident(XMLDocumentFilter)type([]) ident(myfilters) operator(=) operator([)
+ keyword(new) ident(HrefReplaceFilter)operator(()regexp<delimiter(/)content(shergold.html)delimiter(/)>operator(,) regexp<delimiter(/)content(cards.html)delimiter(/)>(\))operator(,)
+ keyword(new) pre_type(Writer)operator(()(\))
+(])
+ident(parser) operator(=) keyword(new) ident(DOMParser)operator(()(\))
+ident(parser)operator(.)ident(setProperty)operator(()string<delimiter(")content(http://cyberneko.org/html/properties/filters)delimiter(")>operator(,) ident(myfilters)(\))
+ident(parser)operator(.)ident(parse)operator(()keyword(new) ident(InputSource)operator(()keyword(new) pre_type(StringReader)operator(()ident(input)(\)\)\))
+comment(//----------------------------------------------------------------------------------)
+
diff --git a/test/scanners/groovy/pleac.in.groovy b/test/scanners/groovy/pleac.in.groovy
new file mode 100644
index 0000000..ddd7641
--- /dev/null
+++ b/test/scanners/groovy/pleac.in.groovy
@@ -0,0 +1,10953 @@
+// -*- groovy -*-
+// The examples make use of Groovy's built-in assert
+// command so that the script is self-checking
+
+// @@PLEAC@@_NAME
+// @@SKIP@@ Groovy
+
+// @@PLEAC@@_WEB
+// @@SKIP@@ http://groovy.codehaus.org
+
+// @@PLEAC@@_1.0
+//----------------------------------------------------------------------------------
+string = '\\n' // two characters, \ and an n
+assert string.size() == 2
+string = "\n" // a "newline" character
+string = '\n' // a "newline" character
+
+string = "Jon 'Maddog' Orwant" // literal single quote inside double quotes
+string = 'Jon \'Maddog\' Orwant' // escaped single quotes
+
+string = 'Jon "Maddog" Orwant' // literal double quotes inside single quotes
+string = "Jon \"Maddog\" Orwant" // escaped double quotes
+
+string = '''
+This is a multiline string declaration
+using single quotes (you can use double quotes)
+'''
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.1
+//----------------------------------------------------------------------------------
+// accessing substrings
+string = 'hippopotamus'
+start = 5; end = 7; endplus1 = 8
+assert string.substring(start, endplus1) == 'pot'
+assert string[start..end] == 'pot'
+
+assert string.substring(start) == 'potamus'
+assert string[start..-1] == 'potamus'
+
+// String is immutable but new strings can be created in various ways
+assert string - 'hippo' - 'mus' + 'to' == 'potato'
+assert string.replace('ppopotam','bisc') == 'hibiscus'
+assert string.substring(0, 2) + 'bisc' + string[-2..-1] == 'hibiscus'
+// StringBuffer is mutable
+sb = new StringBuffer(string)
+sb[2..-3] = 'bisc'
+assert sb.toString() == 'hibiscus'
+
+// No exact pack/unpack equivalents exist in Groovy. Examples here use a custom
+// implementation to split an original string into chunks of specified length
+// the method is a modified version of the Java PLEAC version
+
+// get a 5-character string, skip 8, then grab 2 5-character strings
+// skipping the trailing spaces, then grab the rest
+data = 'hippopotamus means river horse'
+def fields = unpack('A5 x8 A5 x1 A5 x1 A*', data)
+assert fields == ['hippo', 'means', 'river', 'horse']
+
+// On a Java 5 or 6 JVM, Groovy can also make use of Scanners:
+s = new Scanner(data)
+s.findInLine(/(.{5}).{8}(.{5}) (.{5}) (.*)/)
+m = s.match()
+fields = []
+(1..m.groupCount()).each{ fields << m.group(it) }
+assert fields == ['hippo', 'means', 'river', 'horse']
+
+// another scanner example similar to the javadoc example
+input = '1 fish 2 fish red fish blue fish'
+s = new Scanner(input).useDelimiter(/\s*fish\s*/)
+fields = []
+2.times{ fields << s.nextInt() }
+2.times{ fields << s.next() }
+assert fields == [1, 2, 'red', 'blue']
+
+// split at five characters boundaries
+String[] fivers = unpack('A5 ' * (data.length() / 5), data)
+assert fivers == ["hippo", "potam", "us me", "ans r", "iver ", "horse"]
+
+// chop string into individual characters
+assert 'abcd' as String[] == ['a', 'b', 'c', 'd']
+
+string = "This is what you have"
+// Indexing forwards (left to right)
+// tens 000000000011111111112
+// units +012345678901234567890
+// Indexing backwards (right to left)
+// tens 221111111111000000000
+// units 109876543210987654321-
+
+assert string[0] == 'T'
+assert string[5..6] == 'is'
+assert string[13..-1] == 'you have'
+assert string[-1] == 'e'
+assert string[-4..-1] == 'have'
+assert string[-8, -7, -6] == 'you'
+
+data = new StringBuffer(string)
+data[5..6] = "wasn't" ; assert data.toString() == "This wasn't what you have"
+data[-12..-1] = "ondrous" ; assert data.toString() == "This wasn't wondrous"
+data[0..0] = "" ; assert data.toString() == "his wasn't wondrous"
+data[-10..-1] = "" ; assert data.toString() == "his wasn'"
+
+string = "This wasn't wondrous"
+// check last ten characters match some pattern
+assert string[-10..-1] =~ /^t\sw.*s$/
+
+string = 'This is a test'
+assert string[0..4].replaceAll('is', 'at') + string[5..-1] == 'That is a test'
+
+// exchange the first and last letters in a string
+string = 'make a hat'
+string = string[-1] + string[1..-2] + string[0]
+assert string == 'take a ham'
+
+// extract column with unpack
+string = 'To be or not to be'
+
+// skip 6, grab 6
+assert unpack("x6 A6", string) == ['or not']
+
+// forward 6, grab 2, backward 5, grab 2
+assert unpack("x6 A2 X5 A2", string) == ['or', 'be']
+
+assert cut2fmt([8, 14, 20, 26, 30]) == 'A7 A6 A6 A6 A4 A*'
+
+// utility method (derived from Java PLEAC version)
+def unpack(String format, String data) {
+ def result = []
+ int formatOffset = 0, dataOffset = 0
+ int minDataOffset = 0, maxDataOffset = data.size()
+
+ new StringTokenizer(format).each{ token ->
+ int tokenLen = token.length()
+
+ // count determination
+ int count = 0
+ if (tokenLen == 1) count = 1
+ else if (token.charAt(1) == '*') count = -1
+ else count = token[1..-1].toInteger()
+
+ // action determination
+ char action = token.charAt(0)
+ switch (action) {
+ case 'A':
+ if (count == -1) {
+ start = [dataOffset, maxDataOffset].min()
+ result.add(data[start..-1])
+ dataOffset = maxDataOffset
+ } else {
+ start = [dataOffset, maxDataOffset].min()
+ end = [dataOffset + count, maxDataOffset].min()
+ result.add(data[start..<end])
+ dataOffset += count
+ }
+ break
+ case 'x':
+ if (count == -1) dataOffset = maxDataOffset
+ else dataOffset += count
+ break
+ case 'X':
+ if (count == -1) dataOffset = minDataOffset
+ else dataOffset -= count
+ break
+ default:
+ throw new RuntimeException('Unknown action token', formatOffset)
+ }
+ formatOffset += tokenLen + 1
+ }
+ return result as String[]
+}
+
+// utility method
+def cut2fmt(positions) {
+ template = ''
+ lastpos = 1
+ for (pos in positions) {
+ template += 'A' + (pos - lastpos) + ' '
+ lastpos = pos
+ }
+ return template + 'A*'
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.2
+//----------------------------------------------------------------------------------
+// use b if b is true, else c
+b = false; c = 'cat'
+assert (b ? b : c) == 'cat'
+b = true
+assert (b ? b : c)
+// can be simplified to 'b || c' if c is a boolean
+// strictly speaking, b doesn't have to be a boolean,
+// e.g. an empty list is coerced to boolean false
+b = []
+assert (b ? b : c) == 'cat'
+
+// set x to y unless x is already true
+x = false; y = 'dog'
+if (!x) x = y
+assert x == 'dog'
+// can be simplified to 'x ||= y' if y is a boolean
+// x doesn't need to be a boolean, e.g. a non-empty
+// string is coerced to boolean true
+x = 'cat'
+if (!x) x = y
+assert x == 'cat'
+
+// JVM supplies user name
+// otherwise could use exec or built-in Ant features for reading environment vars
+assert System.getProperty('user.name')
+
+// test for nullity then for emptyness
+def setDefaultIfNullOrEmpty(startingPoint) {
+ (!startingPoint || startingPoint.length() == 0) ? 'Greenwich' : startingPoint
+}
+assert setDefaultIfNullOrEmpty(null) == 'Greenwich'
+assert setDefaultIfNullOrEmpty('') == 'Greenwich'
+assert setDefaultIfNullOrEmpty('Something else') == 'Something else'
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.3
+//----------------------------------------------------------------------------------
+v1 = 'alpha'; v2 = 'omega'
+// this can done with explicit swapping via a temp variable
+// or in a slightly more interesting way with a closure
+swap = { temp = v1; v1 = v2; v2 = temp }
+swap()
+assert v1 == 'omega' && v2 == 'alpha'
+// a more generic swap() is also possible using Groovy's metaclass mechanisms
+// but is not idiomatic of Groovy usage
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.4
+//----------------------------------------------------------------------------------
+// char and int are interchangable, apart from precision difference
+// char use 16 bits while int use 32, requiring a cast from int to char
+char ch = 'e'
+int num = ch // no problem
+ch = (char) num // needs an explicit cast
+
+s1 = "Number " + num + " is character " + (char) num
+assert s1 == 'Number 101 is character e'
+s2 = "Character " + ch + " is number " + (int) ch
+assert s2 == 'Character e is number 101'
+
+// easy conversion between char arrays, char lists and Strings
+char[] ascii = "sample".toCharArray() // {115, 97, 109, 112, 108, 101}
+assert new String(ascii) == "sample"
+assert new String([115, 97, 109, 112, 108, 101] as char[]) == "sample"
+
+// convert 'HAL' to 'IBM' (in increasing order of Grooviness)
+assert "HAL".toCharArray().collect{new String(it+1 as char[])}.join() == 'IBM'
+assert ("HAL" as String[]).collect{it.next()}.join() == 'IBM'
+assert "HAL".replaceAll('.', {it.next()}) == 'IBM'
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.5
+//----------------------------------------------------------------------------------
+string = "an apple a day"
+assert string[3..7].split('')[1..5] == ['a', 'p', 'p', 'l', 'e']
+assert string.split('').toList().unique().sort().join() == ' adelnpy'
+
+//----------------------------------------------------------------------------------
+// CheckSum.groovy: Compute 16-bit checksum of input file
+// Usage: groovy CheckSum <file>
+// script:
+checksum = 0
+new File(args[0]).eachByte{ checksum += it }
+checksum %= (int) Math.pow(2, 16) - 1
+println checksum
+//----------------------------------------------------------------------------------
+// to run on its own source code:
+//=> % groovy CheckSum CheckSum.groovy
+//=> 9349
+//----------------------------------------------------------------------------------
+// Slowcat.groovy: Emulate a s l o w line printer
+// Usage: groovy Slowcat <file> <delay_millis_between_each_char>
+// script:
+delay = args[1].toInteger()
+new File(args[0]).eachByte{ print ((char) it); Thread.sleep(delay) }
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.6
+//----------------------------------------------------------------------------------
+assert 'string'.reverse() == 'gnirts'
+
+string = 'Yoda said, "can you see this?"'
+revwords = string.split(' ').toList().reverse().join(' ')
+assert revwords == 'this?" see you "can said, Yoda'
+
+words = ['bob', 'alpha', 'rotator', 'omega', 'reviver']
+long_palindromes = words.findAll{ w -> w == w.reverse() && w.size() > 5 }
+assert long_palindromes == ['rotator', 'reviver']
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.7
+//----------------------------------------------------------------------------------
+s1 = 'abc\t def\tghi \n\tx'
+s2 = 'abc def ghi \n x'
+def expand(s) {
+ s.split('\n').toList().collect{
+ line = it
+ while (line.contains('\t')) {
+ line = line.replaceAll(/([^\t]*)(\t)(.*)/){
+ all,pre,tab,suf -> pre + ' ' * (8 - pre.size() % 8) + suf
+ }
+ }
+ return line
+ }.join('\n')
+}
+def unexpand(s) {
+ s.split('\n').toList().collect{
+ line = it
+ for (i in line.size()-1..1) {
+ if (i % 8 == 0) {
+ prefix = line[0..<i]
+ if (prefix.trim().size() != prefix.size()) {
+ line = prefix.trim() + '\t' + line[i..-1]
+ }
+ }
+ }
+ return line
+ }.join('\n')
+}
+assert expand(s1) == s2
+assert unexpand(s2) == s1
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.8
+//----------------------------------------------------------------------------------
+debt = 150
+assert "You owe $debt to me" == 'You owe 150 to me'
+
+rows = 24; cols = 80
+assert "I am $rows high and $cols wide" == 'I am 24 high and 80 wide'
+
+assert 'I am 17 years old'.replaceAll(/\d+/, {2*it.toInteger()}) == 'I am 34 years old'
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.9
+//----------------------------------------------------------------------------------
+assert "bo peep".toUpperCase() == 'BO PEEP'
+assert 'JOHN'.toLowerCase() == 'john'
+def capitalize(s) {s[0].toUpperCase() + (s.size()<2 ? '' : s[1..-1]?.toLowerCase())}
+assert capitalize('joHn') == 'John'
+
+s = "thIS is a loNG liNE".replaceAll(/\w+/){capitalize(it)}
+assert s == 'This Is A Long Line'
+
+s1 = 'JOhn'; s2 = 'joHN'
+assert s1.equalsIgnoreCase(s2)
+
+private Random rand
+def randomCase(char ch) {
+ (rand.nextInt(100) < 20) ? Character.toLowerCase(ch) : ch
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.10
+//----------------------------------------------------------------------------------
+n = 10
+assert "I have ${n+1} guanacos." == 'I have 11 guanacos.'
+assert "I have " + (n+1) + " guanacos." == 'I have 11 guanacos.'
+
+// sending templated email is solved in two parts: templating and sending
+// Part 1: creating an email template
+naughty = 'Mr Bad Credit'
+def get_manager_list(s) { 'The Big Boss' }
+msg = """
+To: $naughty
+From: Your Bank
+Cc: ${ get_manager_list(naughty) }
+Date: ${ new Date() }
+
+Dear $naughty,
+
+Today, you bounced check number ${ 500 + new Random().nextInt(100) } to us.
+Your account is now closed.
+
+Sincerely,
+the management
+"""
+expected = '''
+To: Mr Bad Credit
+From: Your Bank
+Cc: The Big Boss
+Date: XXX
+
+Dear Mr Bad Credit,
+
+Today, you bounced check number XXX to us.
+Your account is now closed.
+
+Sincerely,
+the management
+'''
+sanitized = msg.replaceAll('(?m)^Date: (.*)$','Date: XXX')
+sanitized = sanitized.replaceAll(/(?m)check number (\d+) to/,'check number XXX to')
+assert sanitized == expected
+// note: Groovy also has several additional built-in templating facilities
+// Part 2: sending email
+// SendMail.groovy: Send email
+// Usage: groovy SendEmail <msgfile>
+// script:
+ant = new AntBuilder()
+ant.mail(from:'manager@grumpybank.com', tolist:'innocent@poorhouse.com',
+ encoding:'plain', mailhost:'mail.someserver.com',
+ subject:'Friendly Letter', message:'this is a test message')
+// Ant has many options for setting encoding, security, attachments, etc., see:
+// http://ant.apache.org/manual/CoreTasks/mail.html
+// Groovy could also use the Java Mail Api directly if required
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.11
+//----------------------------------------------------------------------------------
+def raw = '''
+ your text
+ goes here
+'''
+
+def expected = '''
+your text
+goes here
+'''
+
+assert raw.split('\n').toList().collect{
+ it.replaceAll(/^\s+/,'')
+}.join('\n') + '\n' == expected
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.12
+//----------------------------------------------------------------------------------
+input = '''Folding and splicing is the work of an editor,
+ not a mere collection of silicon
+ and
+ mobile electrons!'''
+
+expected = '''Folding and splicing
+is the work of an
+editor, not a mere
+collection of
+silicon and mobile
+electrons!'''
+
+def wrap(text, maxSize) {
+ all = []
+ line = ''
+ text.eachMatch(/\S+/) {
+ word = it[0]
+ if (line.size() + 1 + word.size() > maxSize) {
+ all += line
+ line = word
+ } else {
+ line += (line == '' ? word : ' ' + word)
+ }
+ }
+ all += line
+ return all.join('\n')
+}
+assert wrap(input, 20) == expected
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.13
+//----------------------------------------------------------------------------------
+string = /Mom said, "Don't do that."/
+// backslash special chars
+assert string.replaceAll(/['"]/){/\\/+it[0]} == /Mom said, \"Don\'t do that.\"/ //'
+// double special chars
+assert string.replaceAll(/['"]/){it[0]+it[0]} == /Mom said, ""Don''t do that.""/ //'
+//backslash quote all non-capital letters
+assert "DIR /?".replaceAll(/[^A-Z]/){/\\/+it[0]} == /DIR\ \/\?/
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.14
+//----------------------------------------------------------------------------------
+assert ' x '.trim() == 'x'
+// print what's typed, but surrounded by >< symbols
+// script:
+new BufferedReader(new InputStreamReader(System.in)).eachLine{
+ println(">" + it.trim() + "<");
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.15
+//----------------------------------------------------------------------------------
+pattern = /"([^\"\\]*(?:\\.[^\"\\]*)*)",?|([^,]+),?|,/
+line = /XYZZY,"","O'Reilly, Inc","Wall, Larry","a \"glug\" bit,",5,"Error, Core Dumped"/
+m = line =~ pattern
+expected = [/XYZZY/, '', /O'Reilly, Inc/, /Wall, Larry/, //'
+ /a \"glug\" bit,/, /5/, /Error, Core Dumped/]
+for (i in 0..<m.size().toInteger())
+ assert expected[i] == (m[i][2] ? m[i][2] : m[i][1])
+
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.16
+//----------------------------------------------------------------------------------
+// A quick google search found several Java implementations.
+// As an example, how to use commons codec is shown below.
+// Just place the respective jar in your classpath.
+// Further details: http://jakarta.apache.org/commons/codec
+// require(groupId:'commons-codec', artifactId:'commons-codec', version:'1.3')
+soundex = new org.apache.commons.codec.language.Soundex()
+assert soundex.soundex('Smith') == soundex.soundex('Smyth')
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.17
+//----------------------------------------------------------------------------------
+input = '''I have analysed the new part. As long as you
+aren't worried about the colour, it is a dropin replacement.''' //'
+
+expected = '''I have analyzed the new part. As long as you
+aren't worried about the color, it is a drop-in replacement.''' //'
+
+translations = [colour:'color', analysed:'analyzed', dropin:'drop-in']
+
+def fixstyle(s) {
+ s.split('\n').toList().collect{
+ line = it
+ translations.each{ key, value ->
+ line = line.replaceAll(/(?<=\W)/ + key + /(?=\W)/, value)
+ }
+ return line
+ }.join('\n')
+}
+assert fixstyle(input) == expected
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_1.18
+//----------------------------------------------------------------------------------
+// Solved in two parts: 'screenscrape' text stream and return stream from process
+// Part 1: text scraping
+input = '''
+ PID PPID PGID WINPID TTY UID STIME COMMAND
+ 4636 1 4636 4636 con 1005 08:24:50 /usr/bin/bash
+ 676 4636 676 788 con 1005 13:53:32 /usr/bin/ps
+'''
+select1 = '''
+ PID PPID PGID WINPID TTY UID STIME COMMAND
+ 676 4636 676 788 con 1005 13:53:32 /usr/bin/ps
+'''
+select2 = '''
+ PID PPID PGID WINPID TTY UID STIME COMMAND
+ 4636 1 4636 4636 con 1005 08:24:50 /usr/bin/bash
+'''
+
+// line below must be configured for your unix - this one's cygwin
+format = cut2fmt([10, 18, 26, 37, 42, 47, 56])
+def psgrep(s) {
+ out = []
+ lines = input.split('\n').findAll{ it.size() }
+ vars = unpack(format, lines[0]).toList().collect{ it.toLowerCase().trim() }
+ out += lines[0]
+ lines[1..-1].each{
+ values = unpack(format, it).toList().collect{
+ try {
+ return it.toInteger()
+ } catch(NumberFormatException e) {
+ return it.trim()
+ }
+ }
+ vars.eachWithIndex{ var, i ->
+ binding.setVariable(var, values[i])
+ }
+ if (new GroovyShell(binding).evaluate(s)) out += it
+ }
+ return '\n' + out.join('\n') + '\n'
+}
+assert psgrep('winpid < 800') == select1
+assert psgrep('uid % 5 == 0 && command =~ /sh$/') == select2
+// Part 2: obtaining text stream from process
+// unixScript:
+input = 'ps'.execute().text
+// cygwinScript:
+input = 'path_to_cygwin/bin/ps.exe'.execute().text
+// windowsScript:
+// can use something like sysinternal.com s pslist (with minor script tweaks)
+input = 'pslist.exe'.execute().text
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.1
+//----------------------------------------------------------------------------------
+// four approaches possible (shown for Integers, similar for floats, double etc.):
+// (1) NumberFormat.getInstance().parse(s) // getInstance() can take locale
+// (2) Integer.parseInt(s)
+// (3) new Integer(s)
+// (4) regex
+import java.text.*
+int nb = 0
+try {
+ nb = NumberFormat.getInstance().parse('33.5') // '.5' will be ignored
+ nb = NumberFormat.getInstance().parse('abc')
+} catch (ParseException ex) {
+ assert ex.getMessage().contains('abc')
+}
+assert nb == 33
+
+try {
+ nb = Integer.parseInt('34')
+ assert nb == 34
+ nb = new Integer('35')
+ nb = Integer.parseInt('abc')
+} catch (NumberFormatException ex) {
+ assert ex.getMessage().contains('abc')
+}
+assert nb == 35
+
+integerPattern = /^[+-]?\d+$/
+assert '-36' =~ integerPattern
+assert !('abc' =~ integerPattern)
+decimalPattern = /^-?(?:\d+(?:\.\d*)?|\.\d+)$/
+assert '37.5' =~ decimalPattern
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.2
+//----------------------------------------------------------------------------------
+// Groovy defaults to BigDecimal if you don't use an explicit float or double
+wage = 5.36
+week = 40 * wage
+assert "One week's wage is: \$$week" == /One week's wage is: $214.40/
+// if you want to use explicit doubles and floats you can still use
+// printf in version 5, 6 or 7 JVMs
+// printf('%5.2f', week as double)
+// => 214.40
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.3
+//----------------------------------------------------------------------------------
+a = 0.255
+b = a.setScale(2, BigDecimal.ROUND_HALF_UP);
+assert a.toString() == '0.255'
+assert b.toString() == '0.26'
+
+a = [3.3 , 3.5 , 3.7, -3.3] as double[]
+// warning rint() rounds to nearest integer - slightly different to Perl's int()
+rintExpected = [3.0, 4.0, 4.0, -3.0] as double[]
+floorExpected = [3.0, 3.0, 3.0, -4.0] as double[]
+ceilExpected = [4.0, 4.0, 4.0, -3.0] as double[]
+a.eachWithIndex{ val, i ->
+ assert Math.rint(val) == rintExpected[i]
+ assert Math.floor(val) == floorExpected[i]
+ assert Math.ceil(val) == ceilExpected[i]
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.4
+//----------------------------------------------------------------------------------
+assert Integer.parseInt('0110110', 2) == 54
+assert Integer.toString(54, 2) == '110110'
+// also works for other radix values, e.g. hex
+assert Integer.toString(60, 16) == '3c'
+
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.5
+//----------------------------------------------------------------------------------
+x = 3; y = 20
+for (i in x..y) {
+ //i is set to every integer from x to y, inclusive
+}
+
+(x..<y).each {
+ //implicit closure variable it is set to every integer from x up to but excluding y
+}
+
+assert (x..y).step(7) == [3, 10, 17]
+
+years = []
+(5..<13).each{ age -> years += age }
+assert years == [5, 6, 7, 8, 9, 10, 11, 12]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.6
+//----------------------------------------------------------------------------------
+// We can add additional methods to the Integer class
+class IntegerCategory {
+ static def romanMap = [1000:'M', 900:'CM', 500:'D', 400:'CD', 100:'C', 90:'XC',
+ 50:'L', 40:'XL', 10:'X', 9:'IX', 5:'V', 4:'IV', 1:'I']
+
+ static getRoman(Integer self) {
+ def remains = self
+ def text = ''
+ romanMap.keySet().sort().reverse().each{ key ->
+ while (remains >= key) {
+ remains -= key
+ text += romanMap[key]
+ }
+ }
+ return text
+ }
+
+ static int parseRoman(Object self, String input) {
+ def ustr = input.toUpperCase()
+ int sum = 0
+ romanMap.keySet().sort().reverse().each{ key ->
+ while (ustr.startsWith(romanMap[key])) {
+ sum += key
+ ustr -= romanMap[key]
+ }
+ }
+ return sum
+ }
+}
+
+use(IntegerCategory) {
+ int fifteen = 15
+ assert fifteen.roman == 'XV'
+ assert parseRoman('XXVI') == 26
+ for (i in 1..3900) {
+ assert i == parseRoman(i.roman)
+ }
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.7
+//----------------------------------------------------------------------------------
+random = new Random()
+100.times{
+ next = random.nextInt(50) + 25
+ assert next > 24
+ assert next < 76
+}
+chars = []
+['A'..'Z','a'..'z','0'..'9',('!@$%^&*' as String[]).toList()].each{chars += it}
+password = (1..8).collect{ chars[random.nextInt(chars.size())] }.join()
+assert password.size() == 8
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.8
+//----------------------------------------------------------------------------------
+// By default Groovy uses Java's Random facilities which use the current time
+// as the initial seed. This always changes but does so slowly over time.
+// You are free to select a better seed if you want greater randomness or
+// use the same one each time if you need repeatability.
+long seed = System.currentTimeMillis()
+random1 = new Random(seed)
+random2 = new Random(seed)
+assert random1.nextInt() == random2.nextInt()
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.9
+//----------------------------------------------------------------------------------
+// java.util.Random which Groovy uses already uses a 48-bit seed
+// You can make use 64 not 48 bits (and make better use of the 48 bits) see here:
+// http://alife.co.uk/nonrandom/
+// You can choose a better seed, e.g. Ant uses:
+seed = System.currentTimeMillis() + Runtime.runtime.freeMemory()
+// You can accept input from the user, e.g.
+// http://examples.oreilly.com/javacrypt/files/oreilly/jonathan/util/Seeder.java
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.10
+//----------------------------------------------------------------------------------
+// use Java's Random.nextGaussian() method
+random = new Random()
+mean = 25
+sdev = 2
+salary = random.nextGaussian() * sdev + mean
+// script:
+printf 'You have been hired at \$%.2f', salary
+// => You have been hired at $25.05
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.11
+//----------------------------------------------------------------------------------
+// radians = Math.toRadians(degrees)
+assert Math.toRadians(90) == Math.PI / 2
+// degrees = Math.toDegrees(radians)
+assert Math.toDegrees(Math.PI) == 180
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.12
+//----------------------------------------------------------------------------------
+// use Java's trigonometry methods in java.lang.Math
+//----------------------------------------------------------------------------------
+t = Math.tan(1.5)
+assert t > 14.1 && t < 14.11
+ac = Math.acos(0.1)
+assert ac > 1.47 && ac < 1.48
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.13
+//----------------------------------------------------------------------------------
+assert Math.log(Math.E) == 1
+assert Math.log10(10000) == 4
+def logn(base, val) { Math.log(val)/Math.log(base) }
+assert logn(2, 1024) == 10
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.14
+//----------------------------------------------------------------------------------
+// there are several Java Matrix packages available, e.g.
+// http://math.nist.gov/javanumerics/jama
+import Jama.Matrix
+matrix1 = new Matrix([
+ [3, 2, 3],
+ [5, 9, 8]
+] as double[][])
+
+matrix2 = new Matrix([
+ [4, 7],
+ [9, 3],
+ [8, 1]
+] as double[][])
+
+expectedArray = [[54.0, 30.0], [165.0, 70.0]] as double[][]
+productArray = matrix1.times(matrix2).array
+
+for (i in 0..<productArray.size()) {
+ assert productArray[i] == expectedArray[i]
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.15
+//----------------------------------------------------------------------------------
+// there are several Java Complex number packages, e.g.:
+// http://jakarta.apache.org/commons/math/userguide/complex.html
+import org.apache.commons.math.complex.Complex
+a = new Complex(3, 5) // 3 + 5i
+b = new Complex(2, -2) // 2 - 2i
+expected = new Complex (16, 4) // 16 + 4i
+assert expected == a * b
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.16
+//----------------------------------------------------------------------------------
+assert Integer.parseInt('101', 16) == 257
+assert Integer.parseInt('077', 8) == 63
+//----------------------------------------------------------------------------------
+// conversionScript:
+print 'Gimme a number in decimal, octal, or hex: '
+reader = new BufferedReader(new InputStreamReader(System.in))
+input = reader.readLine().trim()
+switch(input) {
+ case ~'^0x\\d+':
+ number = Integer.parseInt(input.substring(2), 16); break
+ case ~'^0\\d+':
+ number = Integer.parseInt(input.substring(1), 8); break
+ default:
+ number = Integer.parseInt(input)
+}
+println 'Decimal value: ' + number
+
+// permissionScript:
+print 'Enter file permission in octal: '
+input = new BufferedReader(new InputStreamReader(System.in))
+num = input.readLine().trim()
+permission = Integer.parseInt(num, 8)
+println 'Decimal value: ' + permission
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.17
+//----------------------------------------------------------------------------------
+nf = NumberFormat.getInstance()
+assert nf.format(-1740525205) == '-1,740,525,205'
+//----------------------------------------------------------------------------------
+// @@PLEAC@@_2.18
+//----------------------------------------------------------------------------------
+def timeMessage(hour) { 'It took ' + hour + ' hour' + (hour == 1 ? '' : 's') }
+assert 'It took 1 hour' == timeMessage(1)
+assert 'It took 2 hours' == timeMessage(2)
+
+// you can also use Java's ChoiceFormat
+// overkill for this example but extensible and compatible with MessageFormat
+limits = [1, ChoiceFormat.nextDouble(1)] as double[]
+names = ['century', 'centuries'] as String[]
+choice = new ChoiceFormat(limits, names)
+numCenturies = 1
+expected = 'It took 1 century'
+assert expected == "It took $numCenturies " + choice.format(numCenturies)
+// an alternate constructor syntax
+choice = new ChoiceFormat('0#are no files|1#is one file|2#are multiple files')
+assert choice.format(3) == 'are multiple files'
+
+// more complex pluralization can be done with Java libraries, e.g.:
+// http://www.elvis.ac.nz/brain?PluralizationMapping
+// org.springframework.util.Pluralizer within the Spring Framework (springframework.org)
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_2.19
+//----------------------------------------------------------------------------------
+// calculating prime factors
+def factorize(BigInteger orig) {
+ factors = [:]
+ def addFactor = { x -> if (factors[x]) factors[x] += 1 else factors[x] = 1 }
+ n = orig
+ i = 2
+ sqi = 4 // square of i
+ while (sqi <= n) {
+ while (n.remainder(i) == 0) {
+ n /= i
+ addFactor i
+ }
+ // we take advantage of the fact that (i+1)**2 = i**2 + 2*i + 1
+ sqi += 2 * i + 1
+ i += 1
+ }
+ if ((n != 1) && (n != orig)) addFactor n
+ return factors
+}
+
+def pretty(factors) {
+ if (!factors) return "PRIME"
+ sb = new StringBuffer()
+ factors.keySet().sort().each { key ->
+ sb << key
+ if (factors[key] > 1) sb << "**" + factors[key]
+ sb << " "
+ }
+ return sb.toString().trim()
+}
+
+assert pretty(factorize(2178)) == '2 3**2 11**2'
+assert pretty(factorize(39887)) == 'PRIME'
+assert pretty(factorize(239322000000000000000000)) == '2**19 3 5**18 39887'
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_3.0
+//----------------------------------------------------------------------------------
+// use Date to get the current time
+println new Date()
+// => Mon Jan 01 07:12:32 EST 2007
+// use Calendar to compute year, month, day, hour, minute, and second values
+cal = Calendar.instance
+println 'Today is day ' + cal.get(Calendar.DAY_OF_YEAR) + ' of the current year.'
+// => Today is day 1 of the current year.
+// there are other Java Date/Time packages with extended capabilities, e.g.:
+// http://joda-time.sourceforge.net/
+// there is a special Grails (grails.codehaus.org) time DSL (see below)
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_3.1
+//----------------------------------------------------------------------------------
+cal = Calendar.instance
+Y = cal.get(Calendar.YEAR)
+M = cal.get(Calendar.MONTH) + 1
+D = cal.get(Calendar.DATE)
+println "The current date is $Y $M $D"
+// => The current date is 2006 04 28
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_3.2
+//----------------------------------------------------------------------------------
+// create a calendar with current time and time zone
+cal = Calendar.instance
+// set time zone using long or short timezone values
+cal.timeZone = TimeZone.getTimeZone("America/Los_Angeles")
+cal.timeZone = TimeZone.getTimeZone("UTC")
+// set date fields one at a time
+cal.set(Calendar.MONTH, Calendar.DECEMBER)
+// or several together
+//calendar.set(year, month - 1, day, hour, minute, second)
+// get time in seconds since EPOCH
+long time = cal.time.time / 1000
+println time
+// => 1196522682
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_3.3
+//----------------------------------------------------------------------------------
+// create a calendar with current time and time zone
+cal = Calendar.instance
+// set time
+cal.time = new Date(time * 1000)
+// get date fields
+println('Dateline: '
+ + cal.get(Calendar.HOUR_OF_DAY) + ':'
+ + cal.get(Calendar.MINUTE) + ':'
+ + cal.get(Calendar.SECOND) + '-'
+ + cal.get(Calendar.YEAR) + '/'
+ + (cal.get(Calendar.MONTH) + 1) + '/'
+ + cal.get(Calendar.DATE))
+// => Dateline: 7:33:16-2007/1/1
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_3.4
+//----------------------------------------------------------------------------------
+import java.text.SimpleDateFormat
+long difference = 100
+long after = time + difference
+long before = time - difference
+
+// any field of a calendar is incrementable via add() and roll() methods
+cal = Calendar.instance
+df = new SimpleDateFormat()
+printCal = {cal -> df.format(cal.time)}
+cal.set(2000, 0, 1, 00, 01, 0)
+assert printCal(cal) == '1/01/00 00:01'
+// roll minute back by 2 but don't adjust other fields
+cal.roll(Calendar.MINUTE, -2)
+assert printCal(cal) == '1/01/00 00:59'
+// adjust hour back 1 and adjust other fields if needed
+cal.add(Calendar.HOUR, -1)
+assert printCal(cal) == '31/12/99 23:59'
+
+// larger example
+cal.timeZone = TimeZone.getTimeZone("UTC")
+cal.set(1973, 0, 18, 3, 45, 50)
+cal.add(Calendar.DATE, 55)
+cal.add(Calendar.HOUR_OF_DAY, 2)
+cal.add(Calendar.MINUTE, 17)
+cal.add(Calendar.SECOND, 5)
+assert printCal(cal) == '14/03/73 16:02'
+
+// alternatively, work with epoch times
+long birthTime = 96176750359 // 18/Jan/1973, 3:45:50 am
+long interval = 5 + // 5 second
+ 17 * 60 + // 17 minute
+ 2 * 60 * 60 + // 2 hour
+ 55 * 60 * 60 * 24 // and 55 day
+then = new Date(birthTime + interval * 1000)
+assert df.format(then) == '14/03/73 16:02'
+
+// Alternatively, the Google Data module has a category with DSL-like time support:
+// http://docs.codehaus.org/display/GROOVY/Google+Data+Support
+// which supports the following syntax
+// def interval = 5.seconds + 17.minutes + 2.hours + 55.days
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_3.5
+//----------------------------------------------------------------------------------
+bree = 361535725 // 16 Jun 1981, 4:35:25
+nat = 96201950 // 18 Jan 1973, 3:45:50
+difference = bree - nat
+println "There were $difference seconds between Nat and Bree"
+// => There were 265333775 seconds between Nat and Bree
+seconds = difference % 60
+difference = (difference - seconds) / 60
+minutes = difference % 60
+difference = (difference - minutes) / 60
+hours = difference % 24
+difference = (difference - hours) / 24
+days = difference % 7
+weeks = (difference - days) / 7
+println "($weeks weeks, $days days, $hours:$minutes:$seconds)"
+// => (438 weeks, 4 days, 23:49:35)
+//----------------------------------------------------------------------------------
+cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
+cal.set(1981, 5, 16) // 16 Jun 1981
+date1 = cal.time
+cal.set(1973, 0, 18) // 18 Jan 1973
+date2 = cal.time
+difference = Math.abs(date2.time - date1.time)
+days = difference / (1000 * 60 * 60 * 24)
+assert days == 3071
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_3.6
+//----------------------------------------------------------------------------------
+// create a calendar with current time and time zone
+cal = Calendar.instance
+cal.set(1981, 5, 16)
+yearDay = cal.get(Calendar.DAY_OF_YEAR);
+year = cal.get(Calendar.YEAR);
+yearWeek = cal.get(Calendar.WEEK_OF_YEAR);
+df1 = new SimpleDateFormat("dd/MMM/yy")
+df2 = new SimpleDateFormat("EEEE")
+print(df1.format(cal.time) + ' was a ' + df2.format(cal.time))
+println " and was day number $yearDay and week number $yearWeek of $year"
+// => 16/Jun/81 was a Tuesday and was day number 167 and week number 25 of 1981
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_3.7
+//----------------------------------------------------------------------------------
+input = "1998-06-03"
+df1 = new SimpleDateFormat("yyyy-MM-dd")
+date = df1.parse(input)
+df2 = new SimpleDateFormat("MMM/dd/yyyy")
+println 'Date was ' + df2.format(date)
+// => Date was Jun/03/1998
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_3.8
+//----------------------------------------------------------------------------------
+import java.text.DateFormat
+df = new SimpleDateFormat('E M d hh:mm:ss z yyyy')
+cal.set(2007, 0, 1)
+println 'Customized format gives: ' + df.format(cal.time)
+// => Mon 1 1 09:02:29 EST 2007 (differs depending on your timezone)
+df = DateFormat.getDateInstance(DateFormat.FULL, Locale.FRANCE)
+println 'Customized format gives: ' + df.format(cal.time)
+// => lundi 1 janvier 2007
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_3.9
+//----------------------------------------------------------------------------------
+// script:
+println 'Press return when ready'
+before = System.currentTimeMillis()
+input = new BufferedReader(new InputStreamReader(System.in)).readLine()
+after = System.currentTimeMillis()
+elapsed = (after - before) / 1000
+println "You took $elapsed seconds."
+// => You took2.313 seconds.
+
+// take mean sorting time
+size = 500; number = 100; total = 0
+for (i in 0..<number) {
+ array = []
+ size.times{ array << Math.random() }
+ doubles = array as double[]
+ // sort it
+ long t0 = System.currentTimeMillis()
+ Arrays.sort(doubles)
+ long t1 = System.currentTimeMillis()
+ total += (t1 - t0)
+}
+println "On average, sorting $size random numbers takes ${total / number} milliseconds"
+// => On average, sorting 500 random numbers takes 0.32 milliseconds
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_3.10
+//----------------------------------------------------------------------------------
+delayMillis = 50
+Thread.sleep(delayMillis)
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_3.11
+//----------------------------------------------------------------------------------
+// this could be done more simply using JavaMail's getAllHeaderLines() but is shown
+// in long hand for illustrative purposes
+sampleMessage = '''Delivered-To: alias-someone@somewhere.com.au
+Received: (qmail 27284 invoked from network); 30 Dec 2006 15:16:26 -0000
+Received: from unknown (HELO lists-outbound.sourceforge.net) (66.35.250.225)
+ by bne012m.server-web.com with SMTP; 30 Dec 2006 15:16:25 -0000
+Received: from sc8-sf-list2-new.sourceforge.net (sc8-sf-list2-new-b.sourceforge.net [10.3.1.94])
+ by sc8-sf-spam2.sourceforge.net (Postfix) with ESMTP
+ id D8CCBFDE3; Sat, 30 Dec 2006 07:16:24 -0800 (PST)
+Received: from sc8-sf-mx1-b.sourceforge.net ([10.3.1.91]
+ helo=mail.sourceforge.net)
+ by sc8-sf-list2-new.sourceforge.net with esmtp (Exim 4.43)
+ id 1H0fwX-0003c0-GA
+ for pleac-discuss@lists.sourceforge.net; Sat, 30 Dec 2006 07:16:20 -0800
+Received: from omta05ps.mx.bigpond.com ([144.140.83.195])
+ by mail.sourceforge.net with esmtp (Exim 4.44) id 1H0fwY-0005D4-DD
+ for pleac-discuss@lists.sourceforge.net; Sat, 30 Dec 2006 07:16:19 -0800
+Received: from win2K001 ([138.130.127.127]) by omta05ps.mx.bigpond.com
+ with SMTP
+ id <20061230151611.XVWL19269.omta05ps.mx.bigpond.com@win2K001>;
+ Sat, 30 Dec 2006 15:16:11 +0000
+From: someone@somewhere.com
+To: <pleac-discuss@lists.sourceforge.net>
+Date: Sun, 31 Dec 2006 02:14:57 +1100
+Subject: Re: [Pleac-discuss] C/Posix/GNU - @@pleac@@_10x
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: pleac-discuss-bounces@lists.sourceforge.net
+Errors-To: pleac-discuss-bounces@lists.sourceforge.net
+
+----- Original Message -----
+From: someone@somewhere.com
+To: otherperson@somewhereelse.com
+Cc: <pleac-discuss@lists.sourceforge.net>
+Sent: Wednesday, December 27, 2006 9:18 AM
+Subject: Re: [Pleac-discuss] C/Posix/GNU - @@pleac@@_10x
+
+I really like that description of PLEAC.
+'''
+expected = '''
+Sender Recipient Time Delta
+<origin> somewhere.com 01:14:57 06/12/31
+win2K001 omta05ps.mx.bigpond.com 01:14:57 06/12/31 1m 14s
+omta05ps.mx.bigpond.com mail.sourceforge.net 01:16:11 06/12/31 8s
+sc8-sf-mx1-b.sourceforge. sc8-sf-list2-new.sourcefo 01:16:19 06/12/31 1s
+sc8-sf-list2-new.sourcefo sc8-sf-spam2.sourceforge. 01:16:20 06/12/31 4s
+unknown bne012m.server-web.com 01:16:24 06/12/31 1s
+'''
+
+class MailHopDelta {
+ def headers, firstSender, firstDate, out
+
+ MailHopDelta(mail) {
+ extractHeaders(mail)
+ out = new StringBuffer()
+ def m = (mail =~ /(?m)^Date:\s+(.*)/)
+ firstDate = parseDate(m[0][1])
+ firstSender = (mail =~ /(?m)^From.*\@([^\s>]*)/)[0][1]
+ out('Sender Recipient Time Delta'.split(' '))
+ }
+
+ def parseDate(date) {
+ try {
+ return new SimpleDateFormat('EEE, dd MMM yyyy hh:mm:ss Z').parse(date)
+ } catch(java.text.ParseException ex) {}
+ try {
+ return new SimpleDateFormat('dd MMM yyyy hh:mm:ss Z').parse(date)
+ } catch(java.text.ParseException ex) {}
+ try {
+ return DateFormat.getDateInstance(DateFormat.FULL).parse(date)
+ } catch(java.text.ParseException ex) {}
+ DateFormat.getDateInstance(DateFormat.LONG).parse(date)
+ }
+
+ def extractHeaders(mail) {
+ headers = []
+ def isHeader = true
+ def currentHeader = ''
+ mail.split('\n').each{ line ->
+ if (!isHeader) return
+ switch(line) {
+ case ~/^\s*$/:
+ isHeader = false
+ if (currentHeader) headers << currentHeader
+ break
+ case ~/^\s+.*/:
+ currentHeader += line; break
+ default:
+ if (currentHeader) headers << currentHeader
+ currentHeader = line
+ }
+ }
+ }
+
+ def out(line) {
+ out << line[0][0..<[25,line[0].size()].min()].padRight(26)
+ out << line[1][0..<[25,line[1].size()].min()].padRight(26)
+ out << line[2].padRight(17) + ' '
+ out << line[3] + '\n'
+ }
+
+ def prettyDate(date) {
+ new SimpleDateFormat('hh:mm:ss yy/MM/dd').format(date)
+ }
+
+ def process() {
+ out(['<origin>', firstSender, prettyDate(firstDate), ''])
+ def prevDate = firstDate
+ headers.grep(~/^Received:\sfrom.*/).reverseEach{ hop ->
+ def from = (hop =~ /from\s+(\S+)|\((.*?)\)/)[0][1]
+ def by = (hop =~ /by\s+(\S+\.\S+)/)[0][1]
+ def hopDate = parseDate(hop[hop.lastIndexOf(';')+2..-1])
+ out([from, by, prettyDate(prevDate), prettyDelta(hopDate.time - prevDate.time)])
+ prevDate = hopDate
+ }
+ return out.toString()
+ }
+
+ def prettyField(secs, sign, ch, multiplier, sb) {
+ def whole = (int)(secs / multiplier)
+ if (!whole) return 0
+ sb << '' + (sign * whole) + ch + ' '
+ return whole * multiplier
+ }
+
+ def prettyDelta(millis) {
+ def sign = millis < 0 ? -1 : 1
+ def secs = (int)Math.abs(millis/1000)
+ def sb = new StringBuffer()
+ secs -= prettyField(secs, sign, 'd', 60 * 60 * 24, sb)
+ secs -= prettyField(secs, sign, 'h', 60 * 60, sb)
+ secs -= prettyField(secs, sign, 'm', 60, sb)
+ prettyField(secs, sign, 's', 1, sb)
+ return sb.toString().trim()
+ }
+}
+
+assert '\n' + new MailHopDelta(sampleMessage).process() == expected
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_4.0
+//----------------------------------------------------------------------------------
+simple = [ "this", "that", "the", "other" ]
+nested = [ "this", "that", [ "the", "other" ] ]
+assert nested.size() == 3
+assert nested[2].size() == 2
+
+flattenNestedToSimple = [ "this", "that", [ "the", "other" ] ].flatten()
+assert flattenNestedToSimple.size() == 4
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.1
+//----------------------------------------------------------------------------------
+a = [ "quick", "brown", "fox" ]
+assert a.size() == 3
+a = 'Why are you teasing me?'.split(' ')
+assert a == ["Why", "are", "you", "teasing", "me?"]
+
+removeLeadingSpaces = { it.trim() }
+nonBlankLines = { it }
+lines = '''
+ The boy stood on the burning deck,
+ It was as hot as glass.
+'''.split('\n').collect(removeLeadingSpaces).findAll(nonBlankLines)
+
+assert lines == ["The boy stood on the burning deck,",
+ "It was as hot as glass."]
+
+// initialiseListFromFileScript:
+lines = new File('mydata.txt').readLines()
+
+// processFileScript:
+new File('mydata.txt').eachLine{
+ // dosomething
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.2
+//----------------------------------------------------------------------------------
+marbleColors = ['red', 'green', 'yellow']
+assert marbleColors.join(', ') == 'red, green, yellow'
+
+def commify(items) {
+ if (!items) return items
+ def sepchar = items.find{ it =~ /,/ } ? '; ' : ', '
+ switch (items.size()) {
+ case 1: return items[0]
+ case 2: return items.join(' and ')
+ }
+ items[0..-2].join(sepchar) + sepchar + 'and ' + items[-1]
+}
+
+assert commify(marbleColors) == 'red, green, and yellow'
+
+lists = [
+ [ 'just one thing' ],
+ [ 'Mutt', 'Jeff' ],
+ 'Peter Paul Mary'.split(' '),
+ [ 'To our parents', 'Mother Theresa', 'God' ],
+ [ 'pastrami', 'ham and cheese', 'peanut butter and jelly', 'tuna' ],
+ [ 'recycle tired, old phrases', 'ponder big, happy thoughts' ],
+ [ 'recycle tired, old phrases',
+ 'ponder big, happy thoughts',
+ 'sleep and dream peacefully' ],
+]
+
+expected = '''
+just one thing
+Mutt and Jeff
+Peter, Paul, and Mary
+To our parents, Mother Theresa, and God
+pastrami, ham and cheese, peanut butter and jelly, and tuna
+recycle tired, old phrases and ponder big, happy thoughts
+recycle tired, old phrases; ponder big, happy thoughts; and sleep and dream peacefully
+'''
+
+assert expected == '\n' + lists.collect{commify(it)}.join('\n') + '\n'
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.3
+//----------------------------------------------------------------------------------
+// In Groovy, lists and arrays are more or less interchangeable
+// here is the example using lists
+people = ['Crosby', 'Stills', 'Nash']
+assert people.size() == 3
+people[3] = 'Young'
+assert people.size() == 4
+assert people == ['Crosby', 'Stills', 'Nash', 'Young']
+// to use arrays simply do 'people = peopleArray.toList()' at the start
+// and 'peopleArray = people as String[]' at the end
+// if you attempt to do extension on a Java array you will get an
+// ArrayIndexOutOfBoundsException - which is why Java has ArrayList et al
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.4
+//----------------------------------------------------------------------------------
+// list to process
+people == ['Crosby', 'Stills', 'Nash', 'Young']
+// helper
+startsWithCapital = { word -> word[0] in 'A'..'Z' }
+
+// various styles are possible for processing lists
+// closure style
+people.each { person -> assert startsWithCapital(person) }
+// for loop style
+for (person in people) { assert startsWithCapital(person) }
+
+// unixScriptToFindAllUsersStartingWithLetterA:
+all = 'who'.execute().text.replaceAll('\r', '').split('\n')
+all.grep(~/^a.*/).each{ println it }
+
+// printFileWithWordsReversedScript:
+new File('Pleac/src/SlowCat.groovy').eachLine{ line ->
+ line.split(' ').each{ print it.reverse() }
+}
+
+a = [0.5, 3]; b = [0, 1]
+assert [a, b].flatten().collect{ it * 7 } == [3.5, 21, 0, 7]
+// above doesn't modify original arrays
+// instead use a = a.collect{ ... }
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.5
+//----------------------------------------------------------------------------------
+// not relevant in Groovy since we have always references
+items = []
+for (item in items) {
+ // do something with item
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.6
+//----------------------------------------------------------------------------------
+assert [ 1, 1, 2, 2, 3, 3, 3, 5 ].unique() == [ 1, 2, 3, 5 ]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.7
+//----------------------------------------------------------------------------------
+assert [ 1, 1, 2, 2, 3, 3, 3, 4, 5 ] - [ 1, 2, 4 ] == [3, 3, 3, 5]
+assert [ 1, 1, 2, 2, 3, 3, 3, 4, 5 ].unique() - [ 1, 2, 4 ] == [3, 5]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.8
+//----------------------------------------------------------------------------------
+a = [1, 3, 5, 6, 7, 8]
+b = [2, 3, 5, 7, 9]
+// intersection
+assert a.intersect(b) == [3, 5, 7]
+// union
+assert (a + b).unique().sort() == [1, 2, 3, 5, 6, 7, 8, 9]
+// difference
+assert (a - b) == [1, 6, 8]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.9
+//----------------------------------------------------------------------------------
+members = [ "Time", "Flies" ]
+initiates = [ "An", "Arrow" ]
+members += initiates
+assert members == ["Time", "Flies", "An", "Arrow"]
+
+members.add(2, "Like")
+assert members == ["Time", "Flies", "Like", "An", "Arrow"]
+
+members[0] = "Fruit"
+members[3..4] = ["A", "Banana"]
+assert members == ["Fruit", "Flies", "Like", "A", "Banana"]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.10
+//----------------------------------------------------------------------------------
+items = ["the", "quick", "brown", "fox"]
+assert items.reverse() == ["fox", "brown", "quick", "the"]
+
+firstLetters = []
+items.reverseEach{ firstLetters += it[0] }
+assert firstLetters.join() == 'fbqt'
+
+descending = items.sort().reverse()
+assert descending == ["the", "quick", "fox", "brown"]
+descendingBySecondLastLetter = items.sort { a,b -> b[-2] <=> a[-2] }
+assert descendingBySecondLastLetter == ["brown", "fox", "the", "quick"]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.11
+//----------------------------------------------------------------------------------
+// warning: not an exact equivalent, idiomatic use would return copies
+def shift2 = {one = friends[0]; two = friends[1]; 2.times{friends.remove(0)}}
+friends = 'Peter Paul Mary Jim Tim'.split(' ').toList()
+shift2()
+assert one == 'Peter'
+assert two == 'Paul'
+assert friends == ["Mary", "Jim", "Tim"]
+
+def pop2(items) { items[0..1] }
+beverages = 'Dew Jolt Cola Sprite Fresca'.split(' ').toList()
+pair = pop2(beverages)
+assert pair == ["Dew", "Jolt"]
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_4.12
+//----------------------------------------------------------------------------------
+class Employee {
+ def name
+ def position
+ def salary
+}
+staff = [new Employee(name:'Jim',position:'Manager',salary:26000),
+ new Employee(name:'Jill',position:'Engineer',salary:24000),
+ new Employee(name:'Jack',position:'Engineer',salary:22000)]
+highestEngineer = staff.find { emp -> emp.position == 'Engineer' }
+assert highestEngineer.salary == 24000
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.13
+//----------------------------------------------------------------------------------
+engineers = staff.findAll { e -> e.position == 'Engineer' }
+assert engineers.size() == 2
+
+highPaid = staff.findAll { e -> e.salary > 23000 }
+assert highPaid*.name == ["Jim", "Jill"]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.14
+//----------------------------------------------------------------------------------
+// sort works for numbers
+assert [100, 3, 20].sort() == [3, 20, 100]
+// strings representing numbers will be sorted alphabetically
+assert ['100', '3', '20'].sort() == ["100", "20", "3"]
+// closure style sorting allows arbitrary expressions for the comparison
+assert ['100', '3', '20'].sort{ a,b -> a.toLong() <=> b.toLong()} == ["3", "20", "100"]
+
+// obtain the following on unix systems using: 'ps ux'.execute().text
+processInput = '''
+ PID PPID PGID WINPID TTY UID STIME COMMAND
+ 3868 1 3868 3868 con 1005 06:23:34 /usr/bin/bash
+ 3456 3868 3456 3528 con 1005 06:23:39 /usr/bin/ps
+'''
+nonEmptyLines = {it.trim()}
+lines = processInput.split("\n").findAll(nonEmptyLines)[1..-1]
+def col(n, s) { s.tokenize()[n] }
+commandIdx = 7
+pidIdx = 0
+ppidIdx = 1
+linesByPid = lines.sort{ col(pidIdx,it).toLong() }
+assert col(commandIdx, linesByPid[0]) == '/usr/bin/ps'
+linesByPpid = lines.sort{ col(ppidIdx,it).toLong() }
+assert col(commandIdx, linesByPpid[0]) == '/usr/bin/bash'
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.15
+//----------------------------------------------------------------------------------
+// sort staff from 4.12 by name
+assert staff.sort { a,b -> a.name <=> b.name }*.name == ["Jack", "Jill", "Jim"]
+// sort by first two characters of name and if equal by descending salary
+assert staff.sort { a,b ->
+ astart = a.name[0..1]
+ bstart = b.name[0..1]
+ if (astart == bstart) return b.salary <=> a.salary
+ return astart <=> bstart
+}*.name == ["Jack", "Jim", "Jill"]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.16
+//----------------------------------------------------------------------------------
+items = [1, 2, 3, 4, 5]
+processed = []
+10.times{
+ processed << items[0]
+ items = items[1..-1] + items[0]
+}
+assert processed == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.17
+//----------------------------------------------------------------------------------
+import java.text.DateFormatSymbols as Symbols
+items = new Symbols().shortWeekdays.toList()[1..7]
+assert items == ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
+// not as random as you might expect
+println items.sort{ Math.random() }
+// => ["Sat", "Tue", "Sun", "Wed", "Mon", "Thu", "Fri"]
+// better to use the built-in method for this purpose
+Collections.shuffle(items)
+println items
+// => ["Wed", "Tue", "Fri", "Sun", "Sat", "Thu", "Mon"]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.18
+//----------------------------------------------------------------------------------
+symbols = new Symbols()
+words = symbols.weekdays.toList()[1..7] +
+ symbols.months.toList()[0..11] +
+ symbols.eras.toList() +
+ symbols.amPmStrings.toList()
+
+expected = //
+'AD August February July May October September Tuesday \n' +
+'AM BC Friday June Monday PM Sunday Wednesday \n' +
+'April December January March November Saturday Thursday \n'
+
+class WordFormatter {
+ def cols
+
+ def process(list) {
+ def sb = new StringBuffer()
+ def colWidth = list.max{it.size()}.size() + 1
+ int columns = [cols/colWidth, 1].max()
+ def numWords = list.size()
+ int rows = (numWords + columns - 1) / columns
+ for (row in 0..<rows) {
+ for (col in 0..<columns) {
+ def target = row + col * rows
+ if (target < numWords)
+ sb << list[target].padRight(colWidth)
+ }
+ sb << '\n'
+ }
+ return sb.toString()
+ }
+}
+
+// get nr of chars that fit in window or console, see PLEAC 15.4
+// hard-coded here but several packages are available, e.g. in JLine
+// use a concrete implementation of Terminal.getTerminalWidth()
+def getWinCharWidth() { 80 }
+
+// main script
+actual = new WordFormatter(cols:getWinCharWidth()).process(words.sort())
+assert actual == expected
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_4.19
+//----------------------------------------------------------------------------------
+// recursive version is simplest but can be inefficient
+def fact(n) { (n == 1) ? 1 : n * fact(n-1)}
+assert fact(10) == 3628800
+// unwrapped version: note use of BigInteger
+def factorial(n) {
+ def result = 1G // 1 as BigInteger
+ while (n > 0) {
+ result *= n
+ n -= 1
+ }
+ return result
+}
+expected = 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
+assert expected == factorial(100)
+// println factorial(10000)
+// => 284625... (greater than 35,000 digits)
+
+// simple version but less efficient
+def simplePermute(items, perms) {
+ if (items.size() == 0)
+ println perms.join(' ')
+ else
+ for (i in items) {
+ newitems = items.clone()
+ newperms = perms.clone()
+ newperms.add(i)
+ newitems.remove(i)
+ simplePermute(newitems, newperms)
+ }
+}
+simplePermute(['dog', 'bites', 'man'], [])
+// =>
+//dog bites man
+//dog man bites
+//bites dog man
+//bites man dog
+//man dog bites
+//man bites dog
+
+// optimised version below
+expected = '''
+man bites dog
+man dog bites
+bites man dog
+bites dog man
+dog man bites
+dog bites man
+'''
+
+// n2pat(n, len): produce the N-th pattern of length len
+def n2pat(n, length) {
+ def pat = []
+ int i = 1
+ while (i <= length) {
+ pat << (n % i)
+ n = n.intdiv(i)
+ i += 1
+ }
+ pat
+}
+
+// pat2perm(pat): turn pattern returned by n2pat() into
+// permutation of integers.
+def pat2perm(pat) {
+ def source = (0 ..< pat.size()).collect{ it/*.toString()*/ }
+ def perm = []
+ while (pat.size() > 0) {
+ def next = pat.remove(pat.size()-1)
+ perm << source[next]
+ source.remove(next)
+ }
+ perm
+}
+
+def n2perm(n, len) {
+ pat2perm(n2pat((int)n,len))
+}
+
+data = ['man', 'bites', 'dog']
+sb = new StringBuffer()
+numPermutations = fact(data.size())
+for (j in 0..<numPermutations) {
+ def permutation = n2perm(j, data.size()).collect { k -> data[k] }
+ sb << permutation.join(' ') + '\n'
+}
+assert '\n' + sb.toString() == expected
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.0
+//----------------------------------------------------------------------------------
+// quotes are optional around the key
+age = [ Nat:24, Jules:25, Josh:17 ]
+
+assert age['Nat'] == 24
+// alternate syntax
+assert age."Jules" == 25
+
+foodColor = [
+ Apple: 'red',
+ Banana: 'yellow',
+ Lemon: 'yellow',
+ Carrot: 'orange'
+]
+assert foodColor.size() == 4
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.1
+//----------------------------------------------------------------------------------
+foodColor['Lemon'] = 'green'
+assert foodColor.size() == 4
+assert foodColor['Lemon'] == 'green'
+foodColor['Raspberry'] = 'pink'
+assert foodColor.size() == 5
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.2
+//----------------------------------------------------------------------------------
+assert ['Banana', 'Martini'].collect{ foodColor.containsKey(it)?'food':'drink' } == [ 'food', 'drink' ]
+
+age = [Toddler:3, Unborn:0, Phantasm:null]
+['Toddler', 'Unborn', 'Phantasm', 'Relic'].each{ key ->
+ print "$key: "
+ if (age.containsKey(key)) print 'has key '
+ if (age.containsKey(key) && age[key]!=null) print 'non-null '
+ if (age.containsKey(key) && age[key]) print 'true '
+ println ''
+}
+// =>
+// Toddler: has key non-null true
+// Unborn: has key non-null
+// Phantasm: has key
+// Relic:
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.3
+//----------------------------------------------------------------------------------
+assert foodColor.size() == 5
+foodColor.remove('Banana')
+assert foodColor.size() == 4
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.4
+//----------------------------------------------------------------------------------
+hash = [:]
+hash.each { key, value ->
+ // do something with key and value
+}
+
+hash.each { entry ->
+ // do something with entry
+}
+
+hash.keySet().each { key ->
+ // do something with key
+}
+
+sb = new StringBuffer()
+foodColor.each { food, color ->
+ sb << "$food is $color\n"
+}
+assert '\n' + sb.toString() == '''
+Lemon is green
+Carrot is orange
+Apple is red
+Raspberry is pink
+'''
+
+foodColor.each { entry ->
+ assert entry.key.size() > 4 && entry.value.size() > 2
+}
+
+foodColorsSortedByFood = []
+foodColor.keySet().sort().each { k -> foodColorsSortedByFood << foodColor[k] }
+assert foodColorsSortedByFood == ["red", "orange", "green", "pink"]
+
+fakedInput = '''
+From: someone@somewhere.com
+From: someone@spam.com
+From: someone@somewhere.com
+'''
+
+from = [:]
+fakedInput.split('\n').each{
+ matcher = (it =~ /^From:\s+([^\s>]*)/)
+ if (matcher.matches()) {
+ sender = matcher[0][1]
+ if (from.containsKey(sender)) from[sender] += 1
+ else from[sender] = 1
+ }
+}
+
+// More useful to sort by number of received mail by person
+from.entrySet().sort { a,b -> b.value<=>a.value}.each { e->
+ println "${e.key}: ${e.value}"
+}
+// =>
+// someone@somewhere.com: 2
+// someone@spam.com: 1
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.5
+//----------------------------------------------------------------------------------
+hash = [a:1, b:2, c:3]
+// Map#toString already produce a pretty decent output:
+println hash
+// => ["b":2, "a":1, "c":3]
+
+// Or do it by longhand for customised formatting
+hash.each { k,v -> println "$k => $v" }
+// =>
+// b => 2
+// a => 1
+// c => 3
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.6
+//----------------------------------------------------------------------------------
+// java.util.LinkedHashMap "maintains a doubly-linked list running through all of its entries.
+// This linked list defines the iteration ordering, which is normally the order in which keys
+// were inserted into the map (insertion-order)".
+foodColor = new LinkedHashMap()
+foodColor['Banana'] = 'Yellow'
+foodColor['Apple'] = 'Green'
+foodColor['Lemon'] = 'Yellow'
+
+foodColor.keySet().each{ key -> println key }
+// =>
+// Banana
+// Apple
+// Lemon
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.7
+//----------------------------------------------------------------------------------
+foodsOfColor = [ Yellow:['Banana', 'Lemon'], Green:['Apple'] ]
+foodsOfColor['Green'] += 'Melon'
+assert foodsOfColor == ["Green":["Apple", "Melon"], "Yellow":["Banana", "Lemon"]]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.8
+//----------------------------------------------------------------------------------
+surname = [Mickey: 'Mantle', Babe: 'Ruth']
+assert surname.findAll{ it.value == 'Mantle' }.collect{ it.key } == ["Mickey"]
+
+firstname = [:]
+surname.each{ entry -> firstname[entry.value] = entry.key }
+assert firstname == ["Ruth":"Babe", "Mantle":"Mickey"]
+
+// foodfindScript:
+#!/usr/bin/groovy
+// usage: foodfind food_or_color"
+color = [Apple:'red', Banana:'yellow', Lemon:'yellow', Carrot:'orange']
+given = args[0]
+if (color.containsKey(given))
+ println "$given is a food with color ${color[given]}."
+if (color.containsValue(given)) {
+ // could use commify() here - see 4.2
+ foods = color.findAll{it.value == given}.collect{it.key}
+ join = foods.size() == 1 ? 'is a food' : 'are foods'
+ println "${foods.join(', ')} $join with color ${given}."
+}
+// foodfind red
+// => Apple is a food with color red.
+// foodfind yellow
+// => Lemon, Banana are foods with color yellow.
+// foodfind Carrot
+// => Carrot is a food with color orange.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.9
+//----------------------------------------------------------------------------------
+foodColor = [Apple:'red', Carrot:'orange', Banana:'yellow', Cherry:'black']
+
+// Sorted by keys
+assert foodColor.keySet().sort() == ["Apple", "Banana", "Carrot", "Cherry"]
+// you could now iterate through the hash with the sorted keys
+assert foodColor.values().sort() == ["black", "orange", "red", "yellow"]
+assert foodColor.values().sort{it.size()} == ["red", "black", "orange", "yellow"]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.10
+//----------------------------------------------------------------------------------
+//merged = a.clone.update(b) # because Hash#update changes object in place
+
+drinkColor = [Galliano:'yellow', 'Mai Tai':'blue']
+ingestedColor = [:]
+ingestedColor.putAll(drinkColor)
+// overrides any common keys
+ingestedColor.putAll(foodColor)
+
+totalColors = ingestedColor.values().sort().unique()
+assert totalColors == ["black", "blue", "orange", "red", "yellow"]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.11
+//----------------------------------------------------------------------------------
+foodColor['Lemon']='yellow'
+citrusColor = [Lemon:'yellow', Orange:'orange', Lime:'green']
+println foodColor
+println citrusColor
+common = foodColor.keySet().intersect(citrusColor.keySet())
+assert common == ["Lemon"]
+
+foodButNotCitrus = foodColor.keySet().toList() - citrusColor.keySet().toList()
+assert foodButNotCitrus == ["Carrot", "Apple", "Banana", "Cherry"]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.12
+//----------------------------------------------------------------------------------
+// no problem here, Groovy handles any kind of object for key-ing
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.13
+//----------------------------------------------------------------------------------
+// Groovy uses Java implementations for storing hashes and these
+// support setting an initial capacity and load factor (which determines
+// at what point the hash will be resized if needed)
+hash = [:] // Groovy shorthand gets defaults
+hash = new HashMap() // default capacity and load factor
+println hash.capacity()
+// => 16
+('A'..'Z').each{ hash[it] = it }
+println hash.capacity()
+// => 64
+hash = new HashMap(100) // initial capacity of 100 and default load factor
+hash = new HashMap(100, 0.8f) // initial capacity of 100 and 0.8 load factor
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.14
+//----------------------------------------------------------------------------------
+count = [:]
+letters = []
+foodColor.values().each{ letters.addAll((it as String[]).toList()) }
+letters.each{ if (count.containsKey(it)) count[it] += 1 else count[it] = 1 }
+assert count == ["o":3, "d":1, "k":1, "w":2, "r":2, "c":1, "l":5, "g":1, "b":1, "a":2, "y":2, "n":1, "e":4]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.15
+//----------------------------------------------------------------------------------
+father = [
+ Cain:'Adam',
+ Abel:'Adam',
+ Seth:'Adam',
+ Enoch:'Cain',
+ Irad:'Enoch',
+ Mehujael:'Irad',
+ Methusael:'Mehujael',
+ Lamech:'Methusael',
+ Jabal:'Lamech',
+ Jubal:'Lamech',
+ Tubalcain:'Lamech',
+ Enos:'Seth'
+]
+
+def upline(person) {
+ while (father.containsKey(person)) {
+ print person + ' '
+ person = father[person]
+ }
+ println person
+}
+
+upline('Irad')
+// => Irad Enoch Cain Adam
+
+children = [:]
+father.each { k,v ->
+ if (!children.containsKey(v)) children[v] = []
+ children[v] += k
+}
+def downline(person) {
+ println "$person begat ${children.containsKey(person)?children[person].join(', '):'Nobody'}.\n"
+}
+downline('Tubalcain')
+// => Tubalcain begat Nobody.
+downline('Adam')
+// => Adam begat Abel, Seth, Cain.
+
+// This one doesn't recurse through subdirectories (as a simplification)
+// scriptToFindIncludeFilesWhichContainNoIncludesScript:
+dir = '<path_to_usr/include>'
+includes = [:]
+new File(dir).eachFile{ file ->
+ if (file.directory) return
+ file.eachLine{ line ->
+ matcher = (line =~ '^\\s*#\\s*include\\s*<([^>]+)>')
+ if (matcher.matches()) {
+ if (!includes.containsKey(file.name)) includes[file.name] = []
+ includes[file.name] += matcher[0][1]
+ }
+ }
+}
+// find referenced files which have no includes; assumes all files
+// were processed and none are missing
+println includes.values().sort().flatten().unique() - includes.keySet()
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_5.16
+//----------------------------------------------------------------------------------
+// dutree - print sorted indented rendition of du output
+// obtaining this input is not shown, it is similar to other examples
+// on some unix systems it will be: duProcessFakedInput = "du options".process().text
+duProcessFakedInput = '''
+11732 groovysoap/lib
+68 groovysoap/src/main/groovy/net/soap
+71 groovysoap/src/main/groovy/net
+74 groovysoap/src/main/groovy
+77 groovysoap/src/main
+9 groovysoap/src/examples
+8 groovysoap/src/examples/groovy
+102 groovysoap/src/test
+202 groovysoap/src
+11966 groovysoap
+'''
+
+// The DuNode class collects all information about a directory,
+class DuNode {
+ def name
+ def size
+ def kids = []
+
+ // support for sorting nodes with side
+ def compareTo(node2) { size <=> node2.size }
+
+ def getBasename() {
+ name.replaceAll(/.*\//, '')
+ }
+
+ // returns substring before last "/", otherwise null
+ def getParent() {
+ def p = name.replaceAll(/\/[^\/]+$/,'')
+ return (p == name) ? null : p
+ }
+}
+
+// The DuTree does the actual work of
+// getting the input, parsing it, building up a tree
+// and formatting it for output
+class DuTree {
+ def input
+ def topdir
+ def nodes = [:]
+ def dirsizes = [:]
+ def kids = [:]
+
+ // get a node by name, create it if it does not exist yet
+ def getOrCreateNode(name) {
+ if (!nodes.containsKey(name))
+ nodes[name] = new DuNode(name:name)
+ return nodes[name]
+ }
+
+ // figure out how much is taken in each directory
+ // that isn't stored in the subdirectories. Add a new
+ // fake kid called "." containing that much.
+ def getDots(node) {
+ def cursize = node.size
+ for (kid in node.kids) {
+ cursize -= kid.size
+ getDots(kid)
+ }
+ if (node.size != cursize) {
+ def newnode = getOrCreateNode(node.name + "/.")
+ newnode.size = cursize
+ node.kids += newnode
+ }
+ }
+
+ def processInput() {
+ def name = ''
+ input.split('\n').findAll{it.trim()}.each{ line ->
+ def tokens = line.tokenize()
+ def size = tokens[0]
+ name = tokens[1]
+ def node = getOrCreateNode(name)
+ node.size = size.toInteger()
+ nodes[name] = node
+ def parent = node.parent
+ if (parent)
+ getOrCreateNode(parent).kids << node
+ }
+ topdir = nodes[name]
+ }
+
+ // recursively output everything
+ // passing padding and number width as well
+ // on recursive calls
+ def output(node, prefix='', width=0) {
+ def line = node.size.toString().padRight(width) + ' ' + node.basename
+ println (prefix + line)
+ prefix += line.replaceAll(/\d /, '| ')
+ prefix = prefix.replaceAll(/[^|]/, ' ')
+ if (node.kids.size() > 0) { // not a bachelor node
+ kids = node.kids
+ kids.sort{ a,b -> b.compareTo(a) }
+ width = kids[0].size.toString().size()
+ for (kid in kids) output(kid, prefix, width)
+ }
+ }
+}
+
+tree = new DuTree(input:duProcessFakedInput)
+tree.processInput()
+tree.getDots(tree.topdir)
+tree.output(tree.topdir)
+// =>
+// 11966 groovysoap
+// | 11732 lib
+// | 202 src
+// | | 102 test
+// | | 77 main
+// | | | 74 groovy
+// | | | | 71 net
+// | | | | | 68 soap
+// | | | | | 3 .
+// | | | | 3 .
+// | | | 3 .
+// | | 14 .
+// | | 9 examples
+// | | | 8 groovy
+// | | | 1 .
+// | 32 .
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_6.0
+//----------------------------------------------------------------------------------
+// Groovy has built-in language support for Regular Expressions:
+// * Strings quoted with '/' characters have special escaping
+// rules for backslashes and the like.
+// * ~string (regex pattern operator)
+// * m =~ /pattern/ (regex find operator)
+// * m ==~/pattern/ (regex match operator)
+// * patterns can be used in case expressions in a switch statement
+// * string.replaceAll can take a closure expression as the second argument
+// In addition, Groovy can make use of Java's Pattern, Matcher and Scanner classes
+// directly. (The sugar coating metnioed above sits on top of these anyway).
+// There are also additional open source Java regex libraries which can be used.
+
+meadow1 = 'cow grass butterflies Ovine'
+meadow2 = 'goat sheep flowers dog'
+// pattern strings can benefit from 'slashy' quotes
+partial = /sheep/
+full = /.*sheep.*/
+
+// find operator
+assert !(meadow1 =~ partial)
+assert meadow2 =~ partial
+finder = (meadow2 =~ partial)
+// underneath Groovy sugar coating is Java implementation
+assert finder instanceof java.util.regex.Matcher
+
+// match operator
+assert !(meadow1 ==~ full)
+assert meadow2 ==~ full
+matcher = (meadow2 ==~ full)
+// under the covers is just a boolean
+assert matcher instanceof Boolean
+
+assert meadow1 =~ /(?i)\bovines?\b/ // (?i) == case flag
+
+string = 'good food'
+println string.replaceFirst(/o*/, 'e')
+// => egood food
+println string.replaceAll(/o*/, 'e')
+// => egeede efeede (global)
+// beware this one is just textual replacement
+println string.replace(/o*/, 'e')
+// => good food
+println 'o*o*'.replace(/o*/, 'e')
+// => ee
+
+// groovy -e "m = args[0] =~ /(a|ba|b)+(a|ac)+/; if (m.matches()) println m[0][0]" ababacaca
+// => ababa
+
+digits = "123456789"
+nonlap = digits =~ /\d\d\d/
+assert nonlap.count == 3
+print 'Non-overlapping: '
+(0..<nonlap.count).each{ print nonlap[it] + ' ' }; print '\n'
+print 'Overlapping: '
+yeslap = (digits =~ /(?=(\d\d\d))/)
+assert yeslap.count == 7
+(0..<yeslap.count).each{ print yeslap[it][1] + ' ' }; print '\n'
+// Non-overlapping: 123 456 789
+// Overlapping: 123 234 345 456 567 678 789
+
+string = 'And little lambs eat ivy'
+// Greedy version
+parts = string =~ /(.*)(l[^s]*s)(.*)/
+(1..parts.groupCount()).each{ print "(${parts[0][it]}) " }; print '\n'
+// (And little ) (lambs) ( eat ivy)
+
+// Reluctant version
+parts = string =~ /(.*?)(l[^s]*s)(.*)/
+(1..parts.groupCount()).each{ print "(${parts[0][it]}) " }; print '\n'
+// (And ) (little lambs) ( eat ivy)
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.1
+//----------------------------------------------------------------------------------
+// Groovy splits src and dest to avoid this problem
+src = 'Go this way'
+dst = src.replaceFirst('this', 'that')
+assert dst == 'Go that way'
+
+// extract basename
+src = 'c:/some/path/file.ext'
+dst = src.replaceFirst('^.*/', '')
+assert dst == 'file.ext'
+
+// Make All Words Title-Cased (not that you would do it this way)
+// The preprocessing operations \X where X is one of l, u, L, and U are not supported
+// in the sun regex library but other Java regex libraries may support this. Instead:
+src = 'make all words title-cased'
+dst = src
+('a'..'z').each{ dst = dst.replaceAll(/([^a-zA-Z])/+it+/|\A/+it, /$1/+it.toUpperCase()) }
+assert dst == 'Make All Words Title-Cased'
+
+// rename list of dirs
+bindirs = '/usr/bin /bin /usr/local/bin'.split(' ').toList()
+expected = '/usr/lib /lib /usr/local/lib'.split(' ').toList()
+libdirs = bindirs.collect { dir -> dir.replaceFirst('bin', 'lib') }
+assert libdirs == expected
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.2
+//----------------------------------------------------------------------------------
+// Groovy uses Java regex (other Java regex packages would also be possible)
+// It doesn't support Locale-based settings but you can roll your own to some
+// extent, you can use any Unicode characters as per below and you can use
+// \p{Punct} Punctuation: One of !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
+// or the other special character classes
+words = '''
+silly
+façade
+coöperate
+niño
+Renée
+Moliçre
+hæmoglobin
+naïve
+tschüß
+random!stuff#here\u0948
+'''
+results = ''
+greekAlpha = '\u0391'
+special = 'çéüßöñàæï?' + greekAlpha
+// flag as either Y (alphabetic) or N (not)
+words.split('\n').findAll{it.trim()}.each{ results += it ==~ /^[\w/+special+/]+$/ ?'Y':'N' }
+assert results == 'YYYYYYYYYN'
+results = ''
+words.split('\n').findAll{it.trim()}.each{ results += it ==~ /^[^\p{Punct}]+$/ ?'Y':'N' }
+assert results == 'YYYYYYYYYN'
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.3
+//----------------------------------------------------------------------------------
+// as many non-whitespace bytes as possible
+finder = 'abczqz z' =~ /a\S+z/
+assert finder[0] == 'abczqz'
+
+// as many letters, apostrophes, and hyphens
+finder = "aAzZ'z-z0z" =~ /a[A-Za-z'-]+z/ //'
+assert finder[0] == "aAzZ'z-z"
+
+// selecting words
+finder = '23rd Psalm' =~ /\b([A-Za-z]+)\b/ // usually best
+println finder[0][0]
+// => Psalm (23rd is not matched)
+finder = '23rd Psalm' =~ /\s([A-Za-z]+)\s/ // fails at ends or w/ punctuation
+println finder.matches()
+// => false (no whitespaces at ends)
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.4
+//----------------------------------------------------------------------------------
+str = 'groovy.codehaus.org and www.aboutgroovy.com'
+re = '''(?x) # to enable whitespace and comments
+ ( # capture the hostname in $1
+ (?: # these parens for grouping only
+ (?! [-_] ) # lookahead for neither underscore nor dash
+ [\\w-] + # hostname component
+ \\. # and the domain dot
+ ) + # now repeat that whole thing a bunch of times
+ [A-Za-z] # next must be a letter
+ [\\w-] + # now trailing domain part
+ ) # end of $1 capture
+ '''
+
+finder = str =~ re
+out = str
+(0..<finder.count).each{
+ adr = finder[it][0]
+ out = out.replaceAll(adr, "$adr [${InetAddress.getByName(adr).hostAddress}]")
+}
+println out
+// => groovy.codehaus.org [63.246.7.187] and www.aboutgroovy.com [63.246.7.76]
+
+// to match whitespace or #-characters in an extended re you need to escape them.
+foo = 42
+str = 'blah #foo# blah'
+re = '''(?x) # to enable whitespace and comments
+ \\# # a pound sign
+ (\\w+) # the variable name
+ \\# # another pound sign
+ '''
+finder = str =~ re
+found = finder[0]
+out = str.replaceAll(found[0], evaluate(found[1]).toString())
+assert out == 'blah 42 blah'
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.5
+//----------------------------------------------------------------------------------
+fish = 'One fish two fish red fish blue fish'
+expected = 'The third fish is a red one.'
+thirdFish = /(?:\w+\s+fish\s+){2}(\w+)\s+fish.*/
+assert expected == (fish.replaceAll(thirdFish, 'The third fish is a $1 one.'))
+
+anyFish = /(\w+)\s+fish\b/
+finder = fish =~ anyFish
+// finder contains an array of matched groups
+// 2 = third one (index start at 0), 1 = matched word in group
+out = "The third fish is a ${finder[2][1]} one."
+assert out == expected
+
+evens = []
+(0..<finder.count).findAll{it%2!=0}.each{ evens += finder[it][1] }
+println "Even numbered fish are ${evens.join(' ')}."
+// => Even numbered fish are two blue.
+
+// one of several ways to do this
+pond = fish + ' in the pond'
+fishInPond = (/(\w+)(\s+fish\b\s*)/) * 4 + /(.*)/
+found = (pond =~ fishInPond)[0]
+println ((found[1..6] + 'sushi' + found[8..9]).join())
+// => One fish two fish red fish sushi fish in the pond
+
+// find last fish
+expected = 'Last fish is blue'
+pond = 'One fish two fish red fish blue fish swim here.'
+finder = (pond =~ anyFish)
+assert expected == "Last fish is ${finder[finder.count-1][1]}"
+// => Last fish is blue
+
+// greedy match version of above
+finder = (pond =~ /.*\b/ + anyFish)
+assert expected == "Last fish is ${finder[0][1]}"
+
+// last fish match version of above
+finder = (pond =~ /\b(\w+)\s+fish\b(?!.*\bfish\b)/)
+assert expected == "Last fish is ${finder[0][1]}"
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.6
+//----------------------------------------------------------------------------------
+// Html Stripper
+// get this using: fakedfile = new File('path_to_file.htm').text
+fakedFile = '''
+<html>
+<head><title>Chapter 1 Title</title></head>
+<body>
+<h1>Chapter 1: Some Heading</h1>
+A paragraph.
+</body>
+</html>
+'''
+
+stripExpectations = '''
+Chapter 1 Title
+
+Chapter 1: Some Heading
+A paragraph.
+'''.trim()
+
+stripped = fakedFile.replaceAll(/(?m)<.*?>/,'').trim()
+assert stripExpectations == stripped
+
+pattern = '''(?x)
+ ( # capture in $1
+ Chapter # text string
+ \\s+ # mandatory whitespace
+ \\d+ # decimal number
+ \\s* # optional whitespace
+ : # a real colon
+ . * # anything not a newline till end of line
+ )
+'''
+
+headerfyExpectations = '''
+Chapter 1 Title
+
+<H1>Chapter 1: Some Heading</H1>
+A paragraph.
+'''.trim()
+
+headerfied = stripped.replaceAll(pattern, '<H1>$1</H1>')
+assert headerfyExpectations == headerfied
+
+// one liner equivalent which prints to stdout
+//% groovy -p -e "line.replaceAll(/^(Chapter\s+\d+\s*:.*)/,'<H1>$1</H1>')"
+
+// one liner equivalent which modifies file in place and creates *.bak original file
+//% groovy -pi .bak -e "line.replaceAll(/^(Chapter\s+\d+\s*:.*)/,'<H1>$1</H1>')"
+
+// use: realFileInput = new File(path_to_file).text
+fakeFileInput = '''
+0
+START
+1
+2
+END
+3
+4
+5
+START
+6
+END
+'''
+
+chunkyPattern = /(?ms)^START(.*?)^END/
+finder = fakeFileInput =~ chunkyPattern
+(0..<finder.count).each {
+ println "Chunk #$it contains ${new StringTokenizer(finder[it][1],'\n').countTokens()} lines."
+}
+// =>
+// Chunk #0 contains 2 lines.
+// Chunk #1 contains 1 lines.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.7
+//----------------------------------------------------------------------------------
+// general pattern is:
+//file = new File("datafile").text.split(/pattern/)
+// .Ch, .Se and .Ss divide chunks of input text
+fakedFiletext = '''
+.Ch
+abc
+.Se
+def
+.Ss
+ghi
+.Se
+jkl
+.Se
+mno
+.Ss
+pqr
+.Ch
+stu
+.Ch
+vwx
+.Se
+yz!
+'''
+chunks = fakedFiletext.split(/(?m)^\.(Ch|Se|Ss)$/)
+println "I read ${chunks.size()} chunks."
+// => I read 10 chunks.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.8
+//----------------------------------------------------------------------------------
+// Groovy doesn't support the ~/BEGIN/ .. ~/END/ notation
+// you have to emulate it as shown in the example below
+// The from line number to line number processing is supported
+// from the command line but not within a script, e.g.
+// command-line to print lines 15 through 17 inclusive (see below)
+// > groovy -p -e "if (count in 15..17) return line" datafile
+// Within a script itself, you emulate the count by keeping state
+
+htmlContent = '''
+<h1>A Heading</h1>
+Here is <XMP>inline AAA</XMP>.
+And the bigger Example 2:
+<XMP>
+line BBB
+line CCC
+</XMP>
+Done.
+'''.trim()
+
+examplePattern = /(?ms)<XMP>(.*?)<\/XMP>/
+finder = htmlContent =~ examplePattern
+(0..<finder.count).each {
+ println "Example ${it+1}:"
+ println finder[it][1]
+}
+// =>
+// Example 1:
+// inline AAA
+// Example 2:
+//
+// line BBB
+// line CCC
+//
+
+htmlContent.split('\n').eachWithIndex{ line, count ->
+ if (count in 4..5) println line
+}
+// =>
+// line BBB
+// line CCC
+
+// You would probably use a mail Api for this in Groovy
+fakedMailInput = '''
+From: A Person <someone@somewhere.com>
+To: <pleac-discuss@lists.sourceforge.net>
+Date: Sun, 31 Dec 2006 02:14:57 +1100
+
+From: noone@nowhere.com
+To: <pleac-discuss@lists.sourceforge.net>
+Date: Sun, 31 Dec 2006 02:14:58 +1100
+
+From: someone@somewhere.com
+To: <pleac-discuss@lists.sourceforge.net>
+Date: Sun, 31 Dec 2006 02:14:59 +1100
+'''.trim()+'\n'
+
+seen = [:]
+fakedMailInput.split('\n').each{ line ->
+ m = (line =~ /^From:?\s(.*)/)
+ if (m) {
+ addr = m[0][1] =~ /([^<>(),;\s]+\@[^<>(),;\s]+)/
+ x = addr[0][1]
+ if (seen.containsKey(x)) seen[x] += 1 else seen[x] = 1
+ }
+}
+seen.each{ k,v -> println "Address $k seen $v time${v==1?'':'s'}." }
+// =>
+// Address noone@nowhere.com seen 1 time.
+// Address someone@somewhere.com seen 2 times.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.9
+//----------------------------------------------------------------------------------
+import java.util.regex.Pattern
+
+names = '''
+myFile.txt
+oldFile.tex
+myPicture.jpg
+'''
+
+def glob2pat(globstr) {
+ def patmap = [ '*':'.*', '?':'.', '[':'[', ']':']' ]
+ def result = '(?m)^'
+ '^' + globstr.replaceAll(/(.)/) { all, c ->
+ result += (patmap.containsKey(c) ? patmap[c] : Pattern.quote(c))
+ }
+ result + '$'
+}
+
+def checkNumMatches(pat, count) {
+ assert (names =~ glob2pat(pat)).count == count
+}
+
+checkNumMatches('*.*', 3)
+checkNumMatches('my*.*', 2)
+checkNumMatches('*.t*', 2)
+checkNumMatches('*File.*', 2)
+checkNumMatches('*Rabbit*.*', 0)
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.10
+//----------------------------------------------------------------------------------
+// version 1: simple obvious way
+states = 'CO ON MI WI MN'.split(' ').toList()
+
+def popgrep1(file) {
+ file.eachLine{ line ->
+ if (states.any{ line =~ /\b$it\b/ }) println line
+ }
+}
+// popgrep1(new File('path_to_file'))
+
+// version 2: eval strings; fast but hard to quote (SLOW)
+def popgrep2(file) {
+ def code = 'def found = false\n'
+ states.each{
+ code += "if (!found && line =~ /\\b$it\\b/) found = true\n"
+ }
+ code += "if (found) println line\n"
+ file.eachLine{ line = it; evaluate(code) }
+}
+// popgrep2(new File('path_to_file'))
+
+// version 2b: eval using switch/case (not in Perl cookbook) (SLOW)
+def popgrep2b(file) {
+ def code = 'switch(line) {\n'
+ states.each{
+ code += "case ~/.*\\b$it\\b.*/:\nprintln line;break\n"
+ }
+ code += "default:break\n}\n"
+ file.eachLine{ line = it; evaluate(code) }
+}
+// popgrep2b(new File('path_to_file'))
+
+// version3: build a match_any function as a GString
+def popgrep3(file) {
+ def code = states.collect{ "line =~ /\\b$it\\b/" }.join('||')
+ file.eachLine{ line = it; if (evaluate(code)) println line }
+}
+// popgrep3(new File('path_to_file'))
+
+// version4: pretty fast, but simple: compile all re's first:
+patterns = states.collect{ ~/\b$it\b/ }
+def popgrep4(file) {
+ file.eachLine{ line ->
+ if (patterns.any{ it.matcher(line)}) println line
+ }
+}
+// popgrep4(new File('path_to_file'))
+
+// version5: faster
+str = states.collect{ /\b$it\b/ }.join('|')
+def popgrep5(file) {
+ file.eachLine{ line ->
+ if (line =~ str) println line
+ }
+}
+// popgrep5(new File('path_to_file'))
+
+// version5b: faster (like 5 but compiled outside loop)
+pattern = ~states.collect{ /\b$it\b/ }.join('|')
+def popgrep5b(file) {
+ file.eachLine{ line ->
+ if (pattern.matcher(line)) println line
+ }
+}
+// popgrep5b(new File('path_to_file'))
+
+// speeds trials ON the current source file (~1200 lines)
+// popgrep1 => 0.39s
+// popgrep2 => 25.08s
+// popgrep2b => 23.86s
+// popgrep3 => 22.42s
+// popgrep4 => 0.12s
+// popgrep5 => 0.05s
+// popgrep5b => 0.05s
+// Groovy's built-in support is the way to go in terms of
+// both speed and simplicity of understanding. Avoid using
+// evaluate() unless you absolutely need it
+
+// generic matching functions
+input = '''
+both cat and dog
+neither
+just a cat
+just a dog
+'''.split('\n').findAll{it.trim()}
+
+def matchAny(line, patterns) { patterns.any{ line =~ it } }
+def matchAll(line, patterns) { patterns.every{ line =~ it } }
+
+assert input.findAll{ matchAny(it, ['cat','dog']) }.size() == 3
+assert input.findAll{ matchAny(it, ['cat$','^n.*']) }.size() == 2
+assert input.findAll{ matchAll(it, ['cat','dog']) }.size() == 1
+assert input.findAll{ matchAll(it, ['cat$','^n.*']) }.size() == 0
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.11
+//----------------------------------------------------------------------------------
+// patternCheckingScript:
+prompt = '\n> '
+print 'Enter patterns to check:' + prompt
+new BufferedReader(new InputStreamReader(System.in)).eachLine{ line ->
+ try {
+ Pattern.compile(line)
+ print 'Valid' + prompt
+ } catch (java.util.regex.PatternSyntaxException ex) {
+ print 'Invalid pattern: ' + ex.message + prompt
+ }
+}
+// =>
+// Enter patterns to check:
+// > ab*.c
+// Valid
+// > ^\s+[^a-z]*$
+// Valid
+// > **
+// Invalid pattern: Dangling meta character '*' near index 0
+// **
+// ^
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.12
+//----------------------------------------------------------------------------------
+src = 'dierk könig'
+// simplistic with locale issue
+dst = src
+('a'..'z').each{ dst = dst.replaceAll(/(?<=[^a-zA-Z])/+it+/|\A/+it, it.toUpperCase()) }
+println dst
+// => Dierk KöNig
+// locale avoidance
+dst = src
+('a'..'z').each{ dst = dst.replaceAll(/(?<=\A|\b)/+it, it.toUpperCase()) }
+println dst
+// => Dierk König
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.13
+//----------------------------------------------------------------------------------
+// Several libraries exist, e.g.
+// http://secondstring.sourceforge.net/
+// http://sourceforge.net/projects/simmetrics/
+// both support numerous algorithms. Using the second as an example:
+import uk.ac.shef.wit.simmetrics.similaritymetrics.*
+target = 'balast'
+candidates = '''
+quick
+brown
+fox
+jumped
+over
+the
+lazy
+dog
+ballast
+ballasts
+balustrade
+balustrades
+blast
+blasted
+blaster
+blasters
+blasting
+blasts
+'''.split('\n').findAll{it.trim()}
+metrics = [new Levenshtein(), new MongeElkan(), new JaroWinkler(), new Soundex()]
+def out(name, results) {
+ print name.padLeft(14) + ' '; results.each{print(it.padRight(16))}; println()
+}
+def outr(name, results){out(name, results.collect{''+((int)(it*100))/100})}
+out ('Word/Metric', metrics.collect{it.shortDescriptionString} )
+candidates.each{ w -> outr(w, metrics.collect{ m -> m.getSimilarity(target, w)} )}
+// =>
+// Word/Metric Levenshtein MongeElkan JaroWinkler Soundex
+// quick 0 0.11 0 0.66
+// brown 0.16 0.23 0.5 0.73
+// fox 0 0.2 0 0.66
+// jumped 0 0.2 0 0.66
+// over 0 0.44 0 0.55
+// the 0 0.33 0 0.55
+// lazy 0.33 0.5 0.44 0.66
+// dog 0 0.2 0 0.66
+// ballast 0.85 0.83 0.96 1
+// ballasts 0.75 0.83 0.94 0.94
+// balustrade 0.5 0.93 0.3 0.94
+// balustrades 0.45 0.93 0.3 0.94
+// blast 0.83 0.8 0.88 1
+// blasted 0.57 0.66 0.8 0.94
+// blaster 0.57 0.66 0.8 0.94
+// blasters 0.5 0.66 0.77 0.94
+// blasting 0.5 0.66 0.77 0.94
+// blasts 0.66 0.66 0.84 0.94
+// to implement the example, iterate through /usr/dict/words selecting words
+// where one or a combination of metrics are greater than some threshold
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.14
+//----------------------------------------------------------------------------------
+n = " 49 here"
+println n.replaceAll(/\G /,'0')
+// => 00049 here
+
+str = "3,4,5,9,120"
+print 'Found numbers:'
+str.eachMatch(/\G,?(\d+)/){ print ' ' + it[1] }
+println()
+// => Found numbers: 3 4 5 9 120
+
+// Groovy doesn't have the String.pos or a /c re modifier like Perl
+// But it does have similar functionality. Matcher has start() and
+// end() for find the position and Matcher's usePattern() allows
+// you to swap patterns without changing the buffer position
+text = 'the year 1752 lost 10 days on the 3rd of September'
+p = ~/(?<=\D)(\d+)/
+m = p.matcher(text)
+while (m.find()) {
+ println 'Found ' + m.group() + ' starting at pos ' + m.start() +
+ ' and ending at pos ' + m.end()
+}
+// now reset pos back to between 1st and 2nd numbers
+if (m.find(16)) { println 'Found ' + m.group() }
+// =>
+// Found 1752 starting at pos 9 and ending at pos 13
+// Found 10 starting at pos 19 and ending at pos 21
+// Found 3 starting at pos 34 and ending at pos 35
+// Found 10
+
+// Alternatively you can use Scanner in Java 5-7+:
+p1 = ~/(?<=\D)(\d+)/
+p2 = ~/\S+/
+s = new Scanner(text)
+while ((f = s.findInLine(p1))) { println 'Found: ' + f }
+if ((f = s.findInLine(p2))) { println "Found $f after the last number." }
+// =>
+// Found: 1752
+// Found: 10
+// Found: 3
+// Found rd after the last number.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.15
+//----------------------------------------------------------------------------------
+html = '<b><i>this</i> and <i>that</i> are important</b> Oh, <b><i>me too!</i></b>'
+
+greedyHtmlStripPattern = ~/(?m)<.*>/ // not good
+nonGreedyHtmlStripPattern = ~/(?m)<.*?>/ // not great
+simpleNested = ~/(?mx)<b><i>(.*?)<\/i><\/b>/
+// match BEGIN, then not BEGIN, then END
+generalPattern = ~/BEGIN((?:(?!BEGIN).)*)END/
+betterButInefficient1 = ~/(?mx)<b><i>( (?: (?!<\/b>|<\/i>). )* ) <\/i><\/b>/
+betterButInefficient2 = ~/(?mx)<b><i>( (?: (?!<\/[ib]>). )* ) <\/i><\/b>/
+
+efficientPattern = '''(?mx)
+ <b><i>
+ [^<]* # stuff not possibly bad, and not possibly the end.
+ (?:
+ # at this point, we can have '<' if not part of something bad
+ (?! </?[ib]> ) # what we can't have
+ < # okay, so match the '<'
+ [^<]* # and continue with more safe stuff
+ ) *
+ </i></b>
+''' //'
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.16
+//----------------------------------------------------------------------------------
+input = 'This is a test\nTest of the duplicate word finder.\n'
+dupWordPattern = '''(?ix)
+ \\b # start at word boundary
+ (\\S+) # find chunk of non-whitespace
+ \\b # until a word boundary
+ (
+ \\s+ # followed by whitespace
+ \\1 # and that same chunk again
+ \\b # and a word boundary
+ ) + # one or more times
+'''
+finder = input =~ dupWordPattern
+println 'Found duplicate word: ' + finder[0][1]
+// => Found duplicate word: test
+
+astr = 'nobody'
+bstr = 'bodysnatcher'
+m = "$astr $bstr" =~ /^(\w+)(\w+) \2(\w+)$/
+actual = "${m[0][2]} overlaps in ${m[0][1]}-${m[0][2]}-${m[0][3]}"
+assert actual == 'body overlaps in no-body-snatcher'
+
+cap = 'o' * 180
+while (m = (cap =~ /^(oo+?)\1+$/)) {
+ p1 = m[0][1]
+ print p1.size() + ' '
+ cap = cap.replaceAll(p1,'o')
+}
+println cap.size()
+// => 2 2 3 3 5
+
+// diophantine
+// solve for 12x + 15y + 16z = 281, maximizing x
+if ((m = ('o' * 281) =~ /^(o*)\1{11}(o*)\2{14}(o*)\3{15}$/)) {
+ x=m[0][1].size(); y=m[0][2].size(); z=m[0][3].size()
+ println "One solution is: x=$x; y=$y; z=$z"
+} else println "No solution."
+// => One solution is: x=17; y=3; z=2
+
+// using different quantifiers:
+// /^(o+)\1{11}(o+)\2{14}(o+)\3{15}$/
+// => One solution is: x=17; y=3; z=2
+
+// /^(o*?)\1{11}(o*)\2{14}(o*)\3{15}$/
+// => One solution is: x=0; y=7; z=11
+
+// /^(o+?)\1{11}(o*)\2{14}(o*)\3{15}$/
+// => One solution is: x=1; y=3; z=14
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.17
+//----------------------------------------------------------------------------------
+// Groovy doesn't currently support x!~y so you must use the !(x=~y) style
+
+// alpha OR beta
+assert 'alpha' ==~ /alpha|beta/
+assert 'beta' ==~ /alpha|beta/
+assert 'betalpha' =~ /alpha/ || 'betalpha' =~ /beta/
+
+// alpha AND beta
+assert !('alpha' =~ /(?=.*alpha)(?=.*beta)/)
+assert 'alphabeta' =~ /(?=.*alpha)(?=.*beta)/
+assert 'betalpha' =~ /(?=.*alpha)(?=.*beta)/
+assert 'betalpha' =~ /alpha/ && 'betalpha' =~ /beta/
+
+// alpha AND beta, no overlap
+assert 'alphabeta' =~ /alpha.*beta|beta.*alpha/
+assert !('betalpha' =~ /alpha.*beta|beta.*alpha/)
+
+// NOT beta
+assert 'alpha gamma' =~ /^(?:(?!beta).)*$/
+assert !('alpha beta gamma' =~ /^(?:(?!beta).)*$/)
+
+// NOT bad BUT good
+assert !('GOOD and BAD' =~ /(?=(?:(?!BAD).)*$)GOOD/)
+assert !('BAD' =~ /(?=(?:(?!BAD).)*$)GOOD/)
+assert !('WORSE' =~ /(?=(?:(?!BAD).)*$)GOOD/)
+assert 'GOOD' =~ /(?=(?:(?!BAD).)*$)GOOD/
+
+// minigrep could be done as a one-liner as follows
+// groovy -p -e "if (line =~ /pat/) return line" datafile
+
+string = 'labelled'
+assert string =~ /^(?=.*bell)(?=.*lab)/
+assert string =~ /bell/ && string =~ 'lab'
+fakeAddress = "blah bell blah "
+murrayHillRegex = '''(?x)
+ ^ # start of string
+ (?= # zero-width lookahead
+ .* # any amount of intervening stuff
+ bell # the desired bell string
+ ) # rewind, since we were only looking
+ (?= # and do the same thing
+ .* # any amount of intervening stuff
+ lab # and the lab part
+ )
+'''
+assert string =~ murrayHillRegex
+assert !(fakeAddress =~ murrayHillRegex)
+
+// eliminate overlapping
+assert !(string =~ /(?:^.*bell.*lab)|(?:^.*lab.*bell)/)
+
+brandRegex = '''(?x)
+ (?: # non-capturing grouper
+ ^ .*? # any amount of stuff at the front
+ bell # look for a bell
+ .*? # followed by any amount of anything
+ lab # look for a lab
+ ) # end grouper
+ | # otherwise, try the other direction
+ (?: # non-capturing grouper
+ ^ .*? # any amount of stuff at the front
+ lab # look for a lab
+ .*? # followed by any amount of anything
+ bell # followed by a bell
+ ) # end grouper
+'''
+assert !(string =~ brandRegex)
+
+map = 'the great baldo'
+
+assert map =~ /^(?:(?!waldo).)*$/
+noWaldoRegex = '''(?x)
+ ^ # start of string
+ (?: # non-capturing grouper
+ (?! # look ahead negation
+ waldo # is he ahead of us now?
+ ) # is so, the negation failed
+ . # any character (cuzza /s)
+ ) * # repeat that grouping 0 or more
+ $ # through the end of the string
+'''
+assert map =~ noWaldoRegex
+
+// on unix systems use: realFakedInput = 'w'.process().text
+fakedInput = '''
+ 7:15am up 206 days, 13:30, 4 users, load average: 1.04, 1.07, 1.04
+USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
+tchrist tty1 5:16pm 36days 24:43 0.03s xinit
+tchrist tty2 5:19pm 6days 0.43s 0.43s -tcsh
+tchrist ttyp0 chthon 7:58am 3days 23.44s 0.44s -tcsh
+gnat ttyS4 coprolith 2:01pm 13:36m 0.30s 0.30s -tcsh
+'''.trim() + '\n'
+
+def miniGrepMethod(input) {
+ input.split('\n').findAll{it =~ '^(?!.*ttyp).*tchrist'}
+}
+assert miniGrepMethod(fakedInput).size() == 2
+
+findUserRegex = '''(?xm)
+ ^ # anchored to the start
+ (?! # zero-width look-ahead assertion
+ .* # any amount of anything (faster than .*?)
+ ttyp # the string you don't want to find
+ ) # end look-ahead negation; rewind to start
+ .* # any amount of anything (faster than .*?)
+ tchrist # now try to find Tom
+'''
+assert (fakedInput =~ findUserRegex).count == 2
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.18
+//----------------------------------------------------------------------------------
+// Groovy uses Unicode character encoding
+// special care needs to be taken when using unicode because of the different
+// byte lengths, e.g. à can be encoded as two bytes \u0061\u0300 and is also
+// supported in legacy character sets by a single character \u00E0. To Match
+// this character, you can't use any of /./, /../, /a/, /\u00E0/, /\u0061/\u0300
+// or /\pL/. The correct way is to use /X (not currently supported) or one
+// of /\pL/\pM*/ to ensure that it is a letter or /\PM\pM*/ when you just want
+// to combine multicharacter sequences and don't care whether it is a letter
+def checkUnicode(s) {
+ println s + ' is of size ' + s.size()
+ println 'Exactly matches /./ ' + (s ==~ /./)
+ println 'Exactly matches /../ ' + (s ==~ /../)
+ println 'Exactly matches /a/ ' + (s ==~ /a/)
+ println 'Exactly matches /\\u00E0/ ' + (s ==~ /\u00E0/)
+ println 'Exactly matches /\\u0061\\u0300/ ' + (s ==~ /\u0061\u0300/)
+ println 'Exactly matches /\\pL/ ' + (s ==~ /\pL/)
+ println 'Exactly matches /\\pL\\pM*/ ' + (s ==~ /\pL\pM*/)
+ println 'Exactly matches /\\PM\\pM*/ ' + (s ==~ /\PM\pM*/)
+}
+checkUnicode('à')
+checkUnicode('\u0061\u0300')
+checkUnicode('\u00E0')
+// =>
+// à is of size 1
+// Exactly matches /./ true
+// Exactly matches /../ false
+// Exactly matches /a/ false
+// Exactly matches /\u00E0/ true
+// Exactly matches /\u0061\u0300/ false
+// Exactly matches /\pL/ true
+// Exactly matches /\pL\pM*/ true
+// Exactly matches /\PM\pM*/ true
+// a? is of size 2
+// Exactly matches /./ false
+// Exactly matches /../ true
+// Exactly matches /a/ false
+// Exactly matches /\u00E0/ false
+// Exactly matches /\u0061\u0300/ true
+// Exactly matches /\pL/ false
+// Exactly matches /\pL\pM*/ true
+// Exactly matches /\PM\pM*/ true
+// à is of size 1
+// Exactly matches /./ true
+// Exactly matches /../ false
+// Exactly matches /a/ false
+// Exactly matches /\u00E0/ true
+// Exactly matches /\u0061\u0300/ false
+// Exactly matches /\pL/ true
+// Exactly matches /\pL\pM*/ true
+// Exactly matches /\PM\pM*/ true
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.19
+//----------------------------------------------------------------------------------
+// The Perl Cookbook categorizes this as a hard problem ... mostly for
+// reasons not related to the actual regex - but with a 60-line regex
+// perhaps there are some issues with that too. Further details:
+// http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/ckaddr.gz
+
+simpleCommentStripper = /\([^()]*\)/
+println 'Book Publishing <marketing@books.com> (We will spam you)'.replaceAll(simpleCommentStripper, '')
+// => Book Publishing <marketing@books.com>
+
+// inspired by the fact that domain names can contain any foreign character these days
+modern = /^.+@[^\.].*\.[a-z]{2,}>?$/
+
+// .Net
+lenient = /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/
+
+// a little more checking
+strict = /^[_a-zA-Z0-9- <]+(\.[_a-zA-Z0-9- <]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\./ +
+ /(([0-9]{1,3})|([a-zA-Z]{2,3})|(aero|coop|info|museum|name))>?$/
+
+addresses = ['someuser@somehost.com',
+ 'Book Publishing <marketing@books.com>']
+addresses.each{
+ assert it =~ lenient
+ assert it =~ strict
+ assert it =~ modern
+}
+
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.20
+//----------------------------------------------------------------------------------
+def findAction(ans) {
+ def re = '(?i)^' + Pattern.quote(ans)
+ if ("SEND" =~ re) println "Action is send"
+ else if ("STOP" =~ re) println "Action is stop"
+ else if ("ABORT" =~ re) println "Action is abort"
+ else if ("EDIT" =~ re) println "Action is edit"
+ else println 'No Match'
+}
+findAction('edit something')
+// => No Match
+findAction('edit')
+// => Action is edit
+findAction('se')
+// => Action is send
+findAction('e')
+// => Action is edit
+
+def buildAbbrev(words) {
+ def table = new TreeMap()
+ words.each{ w ->
+ (0..<w.size()).each { n ->
+ if (!(words - w).any{
+ it.size() >= n+1 && it[0..n] == w[0..n]
+ }) table[w[0..n]] = w
+ }
+ }
+ table
+}
+println buildAbbrev('send stop abort edit'.split(' ').toList())
+// => ["a":"abort", "ab":"abort", "abo":"abort", "abor":"abort", "abort":"abort",
+// "e":"edit", "ed":"edit", "edi":"edit", "edit":"edit", "se":"send", "sen":"send",
+// "send":"send", "st":"stop", "sto":"stop", "stop":"stop"]
+
+// miniShellScript:
+// dummy methods
+def invokeEditor() { println "invoking editor" }
+def deliverMessage() { println "delivering message at " + new Date() }
+actions = [
+ edit: this.&invokeEditor,
+ send: this.&deliverMessage,
+ list: { println Runtime.runtime.freeMemory() },
+ abort: { System.exit(0) },
+ unknown: { println "Unknown Command"}
+]
+
+table = buildAbbrev(actions.keySet().toList())
+prompt = '\n> '
+print 'Enter Commands: edit send list abort' + prompt
+new BufferedReader(new InputStreamReader(System.in)).eachLine{ line ->
+ def idx = (table.containsKey(line)) ? table[line] : 'unknown'
+ actions[idx]()
+ print prompt
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.21
+//----------------------------------------------------------------------------------
+//% gunzip -c ~/mail/archive.gz | urlify > archive.urlified
+//% urlify ~/mail/*.inbox > ~/allmail.urlified
+
+urls = '(https?|telnet|gopher|file|wais|ftp|mail)'
+ltrs = /\w/
+gunk = /\#\/~:.?+=&%@!\-/
+punc = /.:?\-/
+doll = /$/
+all = /$ltrs$gunk$punc/
+
+findUrls = """(?ix)
+ \\b # start at word boundary
+ ( # begin group 1 {
+ $urls : # need resource and a colon
+ [$all] +? # followed by on or more of any valid
+ # character, but be conservative and
+ # take only what you need to...
+ ) # end group 1 }
+ (?= # look-ahead non-consumptive assertion
+ [$punc]* # either 0 or more punctuation
+ [^$all] # followed by a non-url character
+ | # or else
+ $doll # then end of the string
+ )
+"""
+
+input = '''
+If you find a typo on http://groovy.codehaus.org please
+send an email to mail:spelling.pedant@codehaus.org
+'''
+
+println input.replaceAll(findUrls,'<a href="$1">$1</a>')
+// =>
+// If you find a typo on <a href="http://groovy.codehaus.org">http://groovy.codehaus.org</a> please
+// send an email to <a href="mail:spelling.pedant@codehaus.org">mail:spelling.pedant@codehaus.org</a>
+
+// urlifyScript:
+#!/usr/bin/groovy
+// urlify - wrap HTML links around URL-like constructs
+// definitions from above
+args.each{ file ->
+ new File(file).eachLine{ line ->
+ println line.replaceAll(findUrls,'<a href="$1">$1</a>')
+ }
+}
+
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.22
+//----------------------------------------------------------------------------------
+// @@INCOMPLETE@@
+// @@INCOMPLETE@@
+
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_6.23
+//----------------------------------------------------------------------------------
+romans = /(?i)^m*(d?c{0,3}|c[dm])(l?x{0,3}|x[lc])(v?i{0,3}|i[vx])$/
+assert 'cmxvi' =~ romans
+// can't have tens before 1000s (M) or 100s (C) after 5s (V)
+assert !('xmvci' =~ romans)
+
+// swap first two words
+assert 'the words'.replaceAll(/(\S+)(\s+)(\S+)/, '$3$2$1') == 'words the'
+
+// extract keyword and value
+m = 'k=v' =~ /(\w+)\s*=\s*(.*)\s*$/
+assert m.matches()
+assert m[0][1] == 'k'
+assert m[0][2] == 'v'
+
+hasAtLeastSize = { n -> /.{$n,}/ }
+assert 'abcdefghijklmnopqrstuvwxyz' =~ hasAtLeastSize(20)
+
+// MM/DD/YY HH:MM:SS (lenient - doesn't check HH > 23 etc)
+d = /\d+/
+datetime = "($d)/($d)/($d) ($d):($d):($d)"
+assert '04/05/2006 10:26:59' =~ datetime
+
+orig = '/usr/bin/vi'
+expected = '/usr/local/bin/vi'
+orig.replaceAll('/usr/bin','/usr/local/bin') == expected
+
+escapeSequenceRegex = /%([0-9A-Fa-f][0-9A-Fa-f])/
+convertEscapeToChar = { Object[] ch -> new Character((char)Integer.parseInt(ch[1],16)) }
+assert 'abc%3cdef'.replaceAll(escapeSequenceRegex, convertEscapeToChar) == 'abc<def'
+
+commentStripper = '''(?xms)
+ /\\* # Match the opening delimiter
+ .* # Match a minimal number of characters */
+ \\*/ # Match the closing delimiter
+'''
+
+input = '''
+a line
+/*
+some comment
+*/
+another line
+'''
+expected = '''
+a line
+
+another line
+'''
+
+assert input.replaceAll(commentStripper,'') == expected
+
+// emulate s.trim()
+assert ' x y '.replaceAll(/^\s+/, '').replaceAll(/\s+$/, '') == 'x y'
+
+// convert \\n into \n
+assert (/a\nb/.replaceAll(/\\n/,"\n") == 'a\nb')
+
+// remove package symbol (Groovy/Java doesn't use this as package symbol)
+assert 'A::B'.replaceAll(/^.*::/, '') == 'B'
+
+// match IP Address (requires leading 0's)
+ipregex = /^([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])\./ +
+ /([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])$/
+assert !('123.456.789' =~ ipregex)
+assert '192.168.000.001' =~ ipregex
+
+// extract basename
+assert 'c:/usr/temp.txt'.replaceAll(/^.*\/{1}/, '') == 'temp.txt'
+
+termcap = ':co#80:li#24:'
+m = (termcap =~ /:co\#(\d+):/)
+assert m.count == 1
+assert m[0][1] == '80'
+
+assert 'cmd c:/tmp/junk.txt'.replaceAll(/ \S+\/{1}/, ' ') == 'cmd junk.txt'
+
+os = System.getProperty('os.name')
+println 'Is Linux? ' + (os ==~ /(?i)linux.*/)
+println 'Is Windows? ' + (os ==~ /(?i)windows.*/)
+println 'Is Mac? ' + (os ==~ /(?i)mac.*/)
+
+// join multiline sting
+multi = '''
+This is
+ a test
+'''.trim()
+assert multi.replaceAll(/(?m)\n\s+/, ' ') == 'This is a test'
+
+// nums in string
+string = 'The 5th test was won today by 10 wickets after 10.5 overs'
+nums = string =~ /(\d+\.?\d*|\.\d+)/
+assert (0..<nums.count).collect{ nums[it][1] }.join(' ') == '5 10 10.5'
+
+// capitalize words
+words = 'the Capital words ARE hiding'
+capwords = words =~ /(\b\p{Upper}+\b)/
+assert (0..<capwords.count).collect{ capwords[it][1] }.join(' ') == 'ARE'
+
+lowords = words =~ /(\b\p{Lower}+\b)/
+assert (0..<lowords.count).collect{ lowords[it][1] }.join(' ') == 'the words hiding'
+
+capWords = words =~ /(\b\p{Upper}\p{Lower}*\b)/
+assert (0..<capWords.count).collect{ capWords[it][1] }.join(' ') == 'Capital'
+
+input = '''
+If you find a typo on <a href="http://groovy.codehaus.org">http://groovy.codehaus.org</a> please
+send an email to <a href="mail:spelling.pedant@codehaus.org">mail:spelling.pedant@codehaus.org</a>
+'''
+
+linkRegex = /(?im)<A[^>]+?HREF\s*=\s*["']?([^'" >]+?)[ '"]?>/ //'
+links = input =~ linkRegex
+(0..<links.count).each{ println links[it][1] }
+// =>
+// http://groovy.codehaus.org
+// mail:spelling.pedant@codehaus.org
+
+// find middle initial if any
+m = 'Lee Harvey Oswald' =~ /^\S+\s+(\S)\S*\s+\S/
+initial = m.count ? m[0][1] : ""
+assert initial == 'H'
+
+// inch marks to quotes
+println 'I said "Hello" to you.'.replaceAll(/"([^"]*)"/, /``$1''/) //"
+// => I said ``Hello'' to you.
+
+// extract sentences (2 spaces or newline after punctuation)
+input = '''
+Is this a sentence?
+Yes! And so
+is this. And the fourth.
+'''
+sentences = []
+strip = input.replaceAll(/(\p{Punct})\n/, '$1 ').replaceAll(/\n/, ' ').replaceAll(/ {3,}/,' ')
+m = strip =~ /(\S.*?\p{Punct})(?= |\Z)/
+(0..<m.count).each{ sentences += m[it][1] }
+assert sentences == ["Is this a sentence?", "Yes!", "And so is this.", "And the fourth."]
+
+// YYYY-MM-DD
+m = '2007-2-28' =~ /(\d{4})-(\d\d?)-(\d\d?)/
+assert m.matches()
+assert ['2007', '2', '28'] == [m[0][1], m[0][2], m[0][3]]
+
+usPhoneRegex = /^[01]?[- .]?(\([2-9]\d{2}\)|[2-9]\d{2})[- .]?\d{3}[- .]?\d{4}$/
+numbers = '''
+(425) 555-0123
+425-555-0123
+425 555 0123
+1-425-555-0123
+'''.trim().split('\n').toList()
+assert numbers.every{ it ==~ usPhoneRegex }
+
+exclaimRegex = /(?i)\boh\s+my\s+gh?o(d(dess(es)?|s?)|odness|sh)\b/
+assert 'Oh my Goodness!' =~ exclaimRegex
+assert !('Golly gosh' =~ exclaimRegex)
+
+input = 'line 1\rline 2\nline\r\nline 3\n\rline 4'
+m = input =~ /(?m)^([^\012\015]*)(\012\015?|\015\012?)/
+assert m.count == 4
+
+
+// @@PLEAC@@_6.22
+// not an exact equivalent to original cookbook but has
+// a reasonable subset of mostly similar functionality
+// instead of -r recursion option, use Ant fileset wildcards
+// e.g. **/*.c. You can also specify an excludes pattern
+// e.g. **/*.* -X **/*.h will process all but header files
+// (currently not optimised and with minimal error checking)
+// uses jopt-simple (jopt-simple.sf.net)
+
+op = new joptsimple.OptionParser()
+NOCASE = 'i'; op.accepts( NOCASE, "case insensitive" )
+WITHN = 'n'; op.accepts( WITHN, "display line/para with line/para number" )
+WITHF = 'H'; op.accepts( WITHF, "display line/para with filename" )
+NONAME = 'h'; op.accepts( NONAME, "hide filenames" )
+COUNT = 'c'; op.accepts( COUNT, "give count of lines/paras matching" )
+TCOUNT = 'C'; op.accepts( TCOUNT, "give count of total matches (multiple per line/para)" )
+WORD = 'w'; op.accepts( WORD, "word boundaries only" )
+EXACT = 'x'; op.accepts( EXACT, "exact matches only" )
+INVERT = 'v'; op.accepts( INVERT, "invert search sense (lines that DON'T match)" )
+EXCLUDE = 'X'; op.accepts( EXCLUDE, "exclude files matching pattern [default is '**/*.bak']" ).
+ withRequiredArg().describedAs('path_pattern')
+MATCH = 'l'; op.accepts( MATCH, "list names of files with matches" )
+NOMATCH = 'L'; op.accepts( NOMATCH, "list names of files with no match" )
+PARA = 'p'; op.accepts( PARA, "para mode (.* matches newlines)" ).
+ withOptionalArg().describedAs('para_pattern')
+EXPR = 'e'; op.accepts( EXPR, "expression (when pattern begins with '-')" ).
+ withRequiredArg().describedAs('pattern')
+FILE = 'f'; op.accepts( FILE, "file containing pattern" ).
+ withRequiredArg().describedAs('filename')
+HELP = 'help'; op.accepts( HELP, "display this message" )
+
+options = op.parse(args)
+params = options.nonOptionArguments()
+if (options.wasDetected( HELP )) {
+ op.printHelpOn( System.out )
+} else if (params.size() == 0) {
+ println "Usage: grep [OPTION]... PATTERN [FILE]...\nTry 'grep --$HELP' for more information."
+} else {
+ modifiers = []
+ paraPattern = ''
+ o_withn = options.wasDetected( WITHN )
+ o_withf = options.wasDetected( WITHF )
+ o_noname = options.wasDetected( NONAME )
+ o_count = options.wasDetected( COUNT )
+ o_tcount = options.wasDetected( TCOUNT )
+ o_invert = options.wasDetected( INVERT )
+ o_match = options.wasDetected( MATCH )
+ o_nomatch = options.wasDetected( NOMATCH )
+ if (options.wasDetected( EXPR )) {
+ pattern = options.valueOf( EXPR )
+ } else if (options.wasDetected( FILE )) {
+ pattern = new File(options.valueOf( FILE )).text.trim()
+ } else {
+ pattern = params[0]
+ params = params[1..-1]
+ }
+ if (options.wasDetected( EXCLUDE )) excludes = options.valueOf( EXCLUDE )
+ else excludes = ['**/*.bak']
+ if (options.wasDetected( EXACT )) pattern = '^' + pattern + '$'
+ else if (options.wasDetected( WORD )) pattern = /\b$pattern\b/
+ if (options.wasDetected( NOCASE )) modifiers += 'i'
+ if (options.wasDetected( PARA )) {
+ if (options.hasArgument( PARA )) paraPattern = options.valueOf( PARA )
+ else paraPattern = '^$'
+ paraPattern = '(?sm)' + paraPattern
+ modifiers += 'sm'
+ }
+ if (modifiers) pattern = "(?${modifiers.join()})" + pattern
+
+ if (params.size() == 0) grepStream(System.in, '<stdin>')
+ else {
+ scanner = new AntBuilder().fileScanner {
+ fileset(dir:'.', includes:params.join(','), excludes:excludes)
+ }
+ for (f in scanner) {
+ grepStream(new FileInputStream(f), f)
+ }
+ }
+}
+
+def grepStream(s, name) {
+ def count = 0
+ def tcount = 0
+ def pieces
+ if (paraPattern) pieces = s.text.split(paraPattern)
+ else pieces = s.readLines()
+ def fileMode = o_match || o_nomatch || o_count || o_tcount
+ pieces.eachWithIndex{line, index ->
+ def m = line =~ pattern
+ boolean found = m.count
+ if (found != o_invert) {
+ count++
+ tcount += m.count
+ if (!fileMode) {
+ linefields = []
+ if (o_withf) linefields += name
+ if (o_withn) linefields += index + 1
+ linefields += line
+ println linefields.join(':')
+ }
+ }
+ }
+ def display = true
+ if ((o_match && count == 0) || (o_nomatch && count != 0)) display = false
+ if (fileMode && display) {
+ filefields = []
+ if (!o_noname) filefields += name
+ if (o_tcount) filefields += tcount
+ else if (o_count) filefields += count
+ println filefields.join(':')
+ }
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_7.0
+//----------------------------------------------------------------------------------
+//testfile = new File('/usr/local/widgets/data') // unix
+testfile = new File('Pleac/data/blue.txt') // windows
+testfile.eachLine{ if (it =~ /blue/) println it }
+
+// Groovy (like Java) uses the File class as an abstraction for
+// the path representing a potential file system resource.
+// Channels and Streams (along with Reader adn Writer helper
+// classes) are used to read and write to files (and other
+// things). Files, channels, streams etc are all "normal"
+// objects; they can be passed around in your programs just
+// like other objects (though there are some restrictions
+// covered elsewhere - e.g. you can't expect to pass a File
+// object between JVMs on different machines running different
+// operating systems and expect them to maintain a meaningful
+// value across the different JVMs). In addition to Streams,
+// there is also support for random access to files.
+
+// Many operations are available on streams and channels. Some
+// return values to indicate success or failure, some can throw
+// exceptions, other times both styles of error reporting may be
+// available.
+
+// Streams at the lowest level are just a sequence of bytes though
+// there are various abstractions at higher levels to allow
+// interacting with streams at encoded character, data type or
+// object levels if desired. Standard streams include System.in,
+// System.out and System.err. Java and Groovy on top of that
+// provide facilities for buffering, filtering and processing
+// streams in various ways.
+
+// File channels provide more powerful operations than streams
+// for reading and writing files such as locks, buffering,
+// positioning, concurrent reading and writing, mapping to memory
+// etc. In the examples which follow, streams will be used for
+// simple cases, channels when more advanced features are
+// required. Groovy currently focusses on providing extra support
+// at the file and stream level rather than channel level.
+// This makes the simple things easy but lets you do more complex
+// things by just using the appropriate Java classes. All Java
+// classes are available within Groovy by default.
+
+// Groovy provides syntactic sugar over the top of Java's file
+// processing capabilities by providing meaning to shorthand
+// operators and by automatically handling scaffolding type
+// code such as opening, closing and handling exceptions behind
+// the scenes. It also provides many powerful closure operators,
+// e.g. file.eachLineMatch(pattern){ some_operation } will open
+// the file, process it line-by-line, finding all lines which
+// match the specified pattern and then invoke some operation
+// for the matching line(s) if any, before closing the file.
+
+
+// this example shows how to access the standard input stream
+// numericCheckingScript:
+prompt = '\n> '
+print 'Enter text including a digit:' + prompt
+new BufferedReader(new InputStreamReader(System.in)).eachLine{ line ->
+ // line is read from System.in
+ if (line =~ '\\d') println "Read: $line" // normal output to System.out
+ else System.err.println 'No digit found.' // this message to System.err
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_7.1
+//----------------------------------------------------------------------------------
+// test values (change for your os and directories)
+inputPath='Pleac/src/pleac7.groovy'; outPath='Pleac/temp/junk.txt'
+
+// For input Java uses InputStreams (for byte-oriented processing) or Readers
+// (for character-oriented processing). These can throw FileNotFoundException.
+// There are also other stream variants: buffered, data, filters, objects, ...
+inputFile = new File(inputPath)
+inputStream = new FileInputStream(inputFile)
+reader = new FileReader(inputFile)
+inputChannel = inputStream.channel
+
+// Examples for random access to a file
+file = new RandomAccessFile(inputFile, "rw") // for read and write
+channel = file.channel
+
+// Groovy provides some sugar coating on top of Java
+println inputFile.text.size()
+// => 13496
+
+// For output Java use OutputStreams or Writers. Can throw FileNotFound
+// or IO exceptions. There are also other flavours of stream: buffered,
+// data, filters, objects, ...
+outFile = new File(outPath)
+appendFlag = false
+outStream = new FileOutputStream(outFile, appendFlag)
+writer = new FileWriter(outFile, appendFlag)
+outChannel = outStream.channel
+
+// Also some Groovy sugar coating
+outFile << 'A Chinese sailing vessel'
+println outFile.text.size() // => 24
+
+// @@PLEAC@@_7.2
+//----------------------------------------------------------------------------------
+// No problem with Groovy since the filename doesn't contain characters with
+// special meaning; like Perl's sysopen. Options are either additional parameters
+// or captured in different classes, e.g. Input vs Output, Buffered vs non etc.
+new FileReader(inputPath)
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_7.3
+//----------------------------------------------------------------------------------
+// '~' is a shell expansion feature rather than file system feature per se.
+// Because '~' is a valid filename character in some operating systems, and Java
+// attempts to be cross-platform, it doesn't automatically expand Tilde's.
+// Given that '~' expansion is commonly used however, Java puts the $HOME
+// environment variable (used by shells to do typical expansion) into the
+// "user.home" system property. This works across operating systems - though
+// the value inside differs from system to system so you shouldn't rely on its
+// content to be of a particular format. In most cases though you should be
+// able to write a regex that will work as expected. Also, Apple's
+// NSPathUtilities can expand and introduce Tildes on platforms it supports.
+path = '~paulk/.cvspass'
+name = System.getProperty('user.name')
+home = System.getProperty('user.home')
+println home + path.replaceAll("~$name(.*)", '$1')
+// => C:\Documents and Settings\Paul/.cvspass
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_7.4
+//----------------------------------------------------------------------------------
+// The exception raised in Groovy reports the filename
+try {
+ new File('unknown_path/bad_file.ext').text
+} catch (Exception ex) {
+ System.err.println(ex.message)
+}
+// =>
+// unknown_path\bad_file.ext (The system cannot find the path specified)
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_7.5
+//----------------------------------------------------------------------------------
+try {
+ temp = File.createTempFile("prefix", ".suffix")
+ temp.deleteOnExit()
+} catch (IOException ex) {
+ System.err.println("Temp file could not be created")
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_7.6
+//----------------------------------------------------------------------------------
+// no special features are provided, here is a way to do it manually
+// DO NOT REMOVE THE FOLLOWING STRING DEFINITION.
+pleac_7_6_embeddedFileInfo = '''
+Script size is 13731
+Last script update: Wed Jan 10 19:05:58 EST 2007
+'''
+ls = System.getProperty('line.separator')
+file = new File('Pleac/src/pleac7.groovy')
+regex = /(?ms)(?<=^pleac_7_6_embeddedFileInfo = ''')(.*)(?=^''')/
+def readEmbeddedInfo() {
+ m = file.text =~ regex
+ println 'Found:\n' + m[0][1]
+}
+def writeEmbeddedInfo() {
+ lastMod = new Date(file.lastModified())
+ newInfo = "${ls}Script size is ${file.size()}${ls}Last script update: ${lastMod}${ls}"
+ file.write(file.text.replaceAll(regex, newInfo))
+}
+readEmbeddedInfo()
+// writeEmbeddedInfo() // uncomment to make script update itself
+// readEmbeddedInfo() // uncomment to redisplay the embedded info after the update
+
+// => (output when above two method call lines are uncommented)
+// Found:
+//
+// Script size is 13550
+// Last script update: Wed Jan 10 18:56:03 EST 2007
+//
+// Found:
+//
+// Script size is 13731
+// Last script update: Wed Jan 10 19:05:58 EST 2007
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_7.7
+//----------------------------------------------------------------------------------
+// general pattern for reading from System.in is:
+// System.in.readLines().each{ processLine(it) }
+
+// general pattern for a filter which can either process file args or read from System.in is:
+// if (args.size() != 0) args.each{
+// file -> new File(file).eachLine{ processLine(it) }
+// } else System.in.readLines().each{ processLine(it) }
+
+// note: the following examples are file-related per se. They show
+// how to do option processing in scenarios which typically also
+// involve file arguments. The reader should also consider using a
+// pre-packaged options parser package (there are several popular
+// ones) rather than the hard-coded processing examples shown here.
+
+chopFirst = false
+columns = 0
+args = ['-c', '-30', 'somefile']
+
+// demo1: optional c
+if (args[0] == '-c') {
+ chopFirst = true
+ args = args[1..-1]
+}
+
+assert args == ["-30", "somefile"]
+assert chopFirst
+
+// demo2: processing numerical options
+if (args[0] =~ /^-(\d+)$/) {
+ columns = args[0][1..-1].toInteger()
+ args = args[1..-1]
+}
+
+assert args == ["somefile"]
+assert columns == 30
+
+// demo3: multiple args (again consider option parsing package)
+args = ['-n','-a','file1','file2']
+nostdout = false
+append = false
+unbuffer = false
+ignore_ints = false
+files = []
+args.each{ arg ->
+ switch(arg) {
+ case '-n': nostdout = true; break
+ case '-a': append = true; break
+ case '-u': unbuffer = true; break
+ case '-i': ignore_ints = true; break
+ default: files += arg
+ }
+}
+if (files.any{ it.startsWith('-')}) {
+ System.err.println("usage: demo3 [-ainu] [filenames]")
+}
+// process files ...
+assert nostdout && append && !unbuffer && !ignore_ints
+assert files == ['file1','file2']
+
+// find login: print all lines containing the string "login" (command-line version)
+//% groovy -ne "if (line =~ 'login') println line" filename
+
+// find login variation: lines containing "login" with line number (command-line version)
+//% groovy -ne "if (line =~ 'login') println count + ':' + line" filename
+
+// lowercase file (command-line version)
+//% groovy -pe "line.toLowerCase()"
+
+
+// count chunks but skip comments and stop when reaching "__DATA__" or "__END__"
+chunks = 0; done = false
+testfile = new File('Pleac/data/chunks.txt') // change on your system
+lines = testfile.readLines()
+for (line in lines) {
+ if (!line.trim()) continue
+ words = line.split(/[^\w#]+/).toList()
+ for (word in words) {
+ if (word =~ /^#/) break
+ if (word in ["__DATA__", "__END__"]) { done = true; break }
+ chunks += 1
+ }
+ if (done) break
+}
+println "Found $chunks chunks"
+
+
+// groovy "one-liner" (cough cough) for turning .history file into pretty version:
+//% groovy -e "m=new File(args[0]).text=~/(?ms)^#\+(\d+)\r?\n(.*?)$/;(0..<m.count).each{println ''+new Date(m[it][1].toInteger())+' '+m[it][2]}" .history
+// =>
+// Sun Jan 11 18:26:22 EST 1970 less /etc/motd
+// Sun Jan 11 18:26:22 EST 1970 vi ~/.exrc
+// Sun Jan 11 18:26:22 EST 1970 date
+// Sun Jan 11 18:26:22 EST 1970 who
+// Sun Jan 11 18:26:22 EST 1970 telnet home
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_7.8
+//----------------------------------------------------------------------------------
+// test data for below
+testPath = 'Pleac/data/process.txt'
+
+// general pattern
+def processWithBackup(inputPath, Closure processLine) {
+ def input = new File(inputPath)
+ def out = File.createTempFile("prefix", ".suffix")
+ out.write('') // create empty file
+ count = 0
+ input.eachLine{ line ->
+ count++
+ processLine(out, line, count)
+ }
+ def dest = new File(inputPath + ".orig")
+ dest.delete() // clobber previous backup
+ input.renameTo(dest)
+ out.renameTo(input)
+}
+
+// use withPrintWriter if you don't want the '\n''s appearing
+processWithBackup(testPath) { out, line, count ->
+ if (count == 20) { // we are at the 20th line
+ out << "Extra line 1\n"
+ out << "Extra line 2\n"
+ }
+ out << line + '\n'
+}
+
+processWithBackup(testPath) { out, line, count ->
+ if (!(count in 20..30)) // skip the 20th line to the 30th
+ out << line + '\n'
+}
+// equivalent to "one-liner":
+//% groovy -i.orig -pe "if (!(count in 20..30)) out << line" testPath
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_7.9
+//----------------------------------------------------------------------------------
+//% groovy -i.orig -pe 'FILTER COMMAND' file1 file2 file3 ...
+
+// the following may also be possible on unix systems (unchecked)
+//#!/usr/bin/groovy -i.orig -p
+// filter commands go here
+
+// "one-liner" templating scenario: change DATE -> current time
+//% groovy -pi.orig -e 'line.replaceAll(/DATE/){new Date()}'
+
+//% groovy -i.old -pe 'line.replaceAll(/\bhisvar\b/, 'hervar')' *.[Cchy] (globbing platform specific)
+
+// one-liner for correcting spelling typos
+//% groovy -i.orig -pe 'line.replaceAll(/\b(p)earl\b/i, '\1erl')' *.[Cchy] (globbing platform specific)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_7.10
+//----------------------------------------------------------------------------------
+// general pattern
+def processFileInplace(file, Closure processText) {
+ def text = file.text
+ file.write(processText(text))
+}
+
+// templating scenario: change DATE -> current time
+testfile = new File('Pleac/data/pleac7_10.txt') // replace on your system
+processFileInplace(testfile) { text ->
+ text.replaceAll(/(?m)DATE/, new Date().toString())
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_7.11
+//----------------------------------------------------------------------------------
+// You need to use Java's Channel class to acquire locks. The exact
+// nature of the lock is somewhat dependent on the operating system.
+def processFileWithLock(file, processStream) {
+ def random = new RandomAccessFile(file, "rw")
+ def lock = random.channel.lock() // acquire exclusive lock
+ processStream(random)
+ lock.release()
+ random.close()
+}
+
+// Instead of an exclusive lock you can acquire a shared lock.
+
+// Also, you can acquire a lock for a region of a file by specifying
+// start and end positions of the region when acquiring the lock.
+
+// For non-blocking functionality, use tryLock() instead of lock().
+def processFileWithTryLock(file, processStream) {
+ random = new RandomAccessFile(file, "rw")
+ channel = random.channel
+ def MAX_ATTEMPTS = 30
+ for (i in 0..<MAX_ATTEMPTS) {
+ lock = channel.tryLock()
+ if (lock != null) break
+ println 'Could not get lock, pausing ...'
+ Thread.sleep(500) // 500 millis = 0.5 secs
+ }
+ if (lock == null) {
+ println 'Unable to acquire lock, aborting ...'
+ } else {
+ processStream(random)
+ lock.release()
+ }
+ random.close()
+}
+
+
+// non-blocking multithreaded example: print first line while holding lock
+Thread.start{
+ processFileWithLock(testfile) { source ->
+ println 'First reader: ' + source.readLine().toUpperCase()
+ Thread.sleep(2000) // 2000 millis = 2 secs
+ }
+}
+processFileWithTryLock(testfile) { source ->
+ println 'Second reader: ' + source.readLine().toUpperCase()
+}
+// =>
+// Could not get lock, pausing ...
+// First reader: WAS LOWERCASE
+// Could not get lock, pausing ...
+// Could not get lock, pausing ...
+// Could not get lock, pausing ...
+// Could not get lock, pausing ...
+// Second reader: WAS LOWERCASE
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_7.12
+//----------------------------------------------------------------------------------
+// In Java, input and output streams have a flush() method and file channels
+// have a force() method (applicable also to memory-mapped files). When creating
+// PrintWriters and // PrintStreams, an autoFlush option can be provided.
+// From a FileInput or Output Stream you can ask for the FileDescriptor
+// which has a sync() method - but you wouldn't you'd just use flush().
+
+inputStream = testfile.newInputStream() // returns a buffered input stream
+autoFlush = true
+printStream = new PrintStream(outStream, autoFlush)
+printWriter = new PrintWriter(outStream, autoFlush)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_7.13
+//----------------------------------------------------------------------------------
+// See the comments in 7.14 about scenarios where non-blocking can be
+// avoided. Also see 7.14 regarding basic information about channels.
+// An advanced feature of the java.nio.channels package is supported
+// by the Selector and SelectableChannel classes. These allow efficient
+// server multiplexing amongst responses from a number of potential sources.
+// Under the covers, it allows mapping to native operating system features
+// supporting such multiplexing or using a pool of worker processing threads
+// much smaller in size than the total available connections.
+//
+// The general pattern for using selectors is:
+//
+// while (true) {
+// selector.select()
+// def it = selector.selectedKeys().iterator()
+// while (it.hasNext()) {
+// handleKey(it++)
+// it.remove()
+// }
+// }
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_7.14
+//----------------------------------------------------------------------------------
+// Groovy has no special support for this apart from making it easier to
+// create threads (see note at end); it relies on Java's features here.
+
+// InputStreams in Java/Groovy block if input is not yet available.
+// This is not normally an issue, because if you have a potential blocking
+// operation, e.g. save a large file, you normally just create a thread
+ // and save it in the background.
+
+// Channels are one way to do non-blocking stream-based IO.
+// Classes which implement the AbstractSelectableChannel interface provide
+// a configureBlocking(boolean) method as well as an isBlocking() method.
+// When processing a non-blocking stream, you need to process incoming
+// information based on the number of bytes read returned by the various
+// read methods. For non-blocking, this can be 0 bytes even if you pass
+// a fixed size byte[] buffer to the read method. Non-blocking IO is typically
+// not used with Files but more normally with network streams though they
+// can when Pipes (couple sink and source channels) are involved where
+// one side of the pipe is a file.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_7.15
+//----------------------------------------------------------------------------------
+// Groovy uses Java's features here.
+// For both blocking and non-blocking reads, the read operation returns the number
+// of bytes read. In blocking operations, this normally corresponds to the number
+// of bytes requested (typically the size of some buffer) but can have a smaller
+// value at the end of a stream. Java also makes no guarantees about whether
+// other streams in general will return bytes as they become available under
+// certain circumstances (rather than blocking until the entire buffer is filled.
+// In non-blocking operations, the number of bytes returned will typically be
+// the number of bytes available (up to some maximum buffer or requested size).
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_7.16
+//----------------------------------------------------------------------------------
+// This just works in Java and Groovy as per the previous examples.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_7.17
+//----------------------------------------------------------------------------------
+// Groovy uses Java's features here.
+// More work has been done in the Java on object caching than file caching
+// with several open source and commercial offerings in that area. File caches
+// are also available, for one, see:
+// http://portals.apache.org/jetspeed-1/apidocs/org/apache/jetspeed/cache/FileCache.html
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_7.18
+//----------------------------------------------------------------------------------
+// The general pattern is: streams.each{ stream -> stream.println 'item to print' }
+// See the MultiStream example in 13.5 for a coded example.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_7.19
+//----------------------------------------------------------------------------------
+// You wouldn't normally be dealing with FileDescriptors. In case were you have
+// one you would normally walk through all known FileStreams asking each for
+// it's FileDescriptor until you found one that matched. You would then close
+// that stream.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_7.20
+//----------------------------------------------------------------------------------
+// There are several concepts here. At the object level, any two object references
+// can point to the same object. Any changes made by one of these will be visible
+// in the 'alias'. You can also have multiple stream, reader, writer or channel objects
+// referencing the same resource. Depending on the kind of resource, any potential
+// locks, the operations being requested and the behaviour of third-party programs,
+// the result of trying to perform such concurrent operations may not always be
+// deterministic. There are strategies for coping with such scenarious but the
+// best bet is to avoid the issue.
+
+// For the scenario given, copying file handles, that corresponds most closely
+// with cloning streams. The best bet is to just use individual stream objects
+// both created from the same file. If you are attempting to do write operations,
+// then you should consider using locks.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_7.21
+//----------------------------------------------------------------------------------
+// locking is built in to Java (since 1.4), so should not be missing
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_7.22
+//----------------------------------------------------------------------------------
+// Java locking supports locking just regions of files.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.0
+//----------------------------------------------------------------------------------
+datafile = new File('Pleac/data/pleac8_0.txt') // change on your system
+
+datafile.eachLine{ line -> print line.size() }
+
+lines = datafile.readLines()
+
+wholeTextFile = datafile.text
+
+// on command line Groovy use -a auto split pattern instead of record separator
+// default pattern is /\s/
+// groovy -a -e 'println "First word is ${split[0][1]}"'
+
+// (additional examples to original cookbook to illustrate -a)
+// Print processes owned by root:
+// ps aux|groovy -ane "if(split[0][1] =~ 'root')println split[0][10..-1]"
+
+// Print all logins from /etc/passwd that are not commented:
+// groovy -a':' -ne "if(!(split[0][1] =~ /^#/))println split[0][1]" /etc/passwd
+
+// Add the first and the penultimate column of a file:
+// groovy -ape "split[0][1].toInteger()+split[0][-2].toInteger()" accounts.txt
+
+// no BEGIN and END in Groovy (has been proposed, may be added soon)
+
+datafile.withOutputStream{ stream ->
+ stream.print "one" + "two" + "three" // "onetwothree" -> file
+ println "Baa baa black sheep." // sent to $stdout
+}
+
+// use streams or channels for advanced file handling
+int size = datafile.size()
+buffer = ByteBuffer.allocate(size) // for large files, use some block size, e.g. 4096
+channel = new FileInputStream(datafile).channel
+println "Number of bytes read was: ${channel.read(buffer)}" // -1 = EOF
+
+channel = new FileOutputStream(File.createTempFile("pleac8", ".junk")).channel
+size = channel.size()
+channel.truncate(size) // shrinks file (in our case to same size)
+
+pos = channel.position()
+println "I'm $pos bytes from the start of datafile"
+channel.position(pos) // move to pos (in our case unchanged)
+channel.position(0) // move to start of file
+channel.position(size) // move to end of file
+
+// no sysread and syswrite are available but dataInput/output streams
+// can be used to achieve similar functionality, see 8.15.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.1
+//----------------------------------------------------------------------------------
+testfile = new File('Pleac/data/pleac8_1.txt') // change on your system
+// contents of testfile:
+// DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) \
+// $(TEXINFOS) $(INFOS) $(MANS) $(DATA)
+// DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) \
+// $(TEXINFOS) $(INFO_DEPS) $(MANS) $(DATA) \
+// $(EXTRA_DIST)
+
+lines = []
+continuing = false
+regex = /\\$/
+testfile.eachLine{ line ->
+ stripped = line.replaceAll(regex,'')
+ if (continuing) lines[-1] += stripped
+ else lines += stripped
+ continuing = (line =~ regex)
+}
+println lines.join('\n')
+// =>
+// DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(INFOS) $(MANS) $(DATA)
+// DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(INFO_DEPS) $(MANS) $(DATA) $(EXTRA_DIST)
+
+// to remove hidden spaces after the slash (but keep the slash):
+def trimtail(line) {
+ line = line.replaceAll(/(?<=\\)\s*$/, '')
+}
+b = /\\/ // backslash
+assert "abc $b" == trimtail("abc $b")
+assert "abc " == trimtail("abc ")
+assert "abc $b" == trimtail("abc $b ")
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.2
+//----------------------------------------------------------------------------------
+// unixScript:
+println ("wc -l < $filename".execute().text)
+
+// for small files which fit in memory
+println testfile.readLines().size()
+
+// streaming approach (lines and paras)
+lines = 0; paras = 1
+testfile.eachLine{ lines++; if (it =~ /^$/) paras++ }
+println "Found $lines lines and $paras paras."
+// note: counts blank line at end as start of next empty para
+
+// with a StreamTokenizer
+st = new StreamTokenizer(testfile.newReader())
+while (st.nextToken() != StreamTokenizer.TT_EOF) {}
+println st.lineno()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.3
+//----------------------------------------------------------------------------------
+// general pattern
+def processWordsInFile(file, processWord) {
+ testfile.splitEachLine(/\W+/) { matched ->
+ matched.each{ w -> if (w) processWord(w) }
+ }
+}
+
+testfile = new File('Pleac/src/pleac8.groovy') // change path on your system
+
+// count words
+count = 0
+processWordsInFile(testfile){ count++ }
+println count
+
+// (variation to Perl example)
+// with a StreamTokenizer (counting words and numbers in Pleac chapter 8 source file)
+words = 0; numbers = 0
+st = new StreamTokenizer(testfile.newReader())
+st.slashSlashComments(true) // ignore words and numbers in comments
+while (st.nextToken() != StreamTokenizer.TT_EOF) {
+ if (st.ttype == StreamTokenizer.TT_WORD) words++
+ else if (st.ttype == StreamTokenizer.TT_NUMBER) numbers++
+}
+println "Found $words words and $numbers numbers."
+
+
+// word frequency count
+seen = [:]
+processWordsInFile(testfile) {
+ w = it.toLowerCase()
+ if (seen.containsKey(w)) seen[w] += 1
+ else seen[w] = 1
+}
+// output map in a descending numeric sort of its values
+seen.entrySet().sort { a,b -> b.value <=> a.value }.each{ e ->
+ printf("%5d %s\n", [e.value, e.key] )
+}
+// =>
+// 25 pleac
+// 22 line
+// 20 file
+// 19 println
+// 19 lines
+// 13 testfile
+// ...
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.4
+//----------------------------------------------------------------------------------
+testfile.readLines().reverseEach{
+ println it
+}
+
+lines = testfile.readLines()
+// normally one would use the reverseEach, but you can use
+// a numerical index if you want
+((lines.size() - 1)..0).each{
+ println lines[it]
+}
+
+// Paragraph-based processing could be done as in 8.2.
+
+// A streaming-based solution could use random file access
+// and have a sliding buffer working from the back of the
+// file to the front.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.5
+//----------------------------------------------------------------------------------
+logfile = new File('Pleac/data/sampleLog.txt')
+// logTailingScript:
+sampleInterval = 2000 // 2000 millis = 2 secs
+file = new RandomAccessFile( logfile, "r" )
+filePointer = 0 // set to logfile.size() to begin tailing from the end of the file
+while( true ) {
+ // Compare the length of the file to the file pointer
+ long fileLength = logfile.size()
+ if( fileLength < filePointer ) {
+ // Log file must have been rotated or deleted;
+ System.err.println "${new Date()}: Reopening $logfile"
+ file = new RandomAccessFile( logfile, "r" )
+ filePointer = 0
+ }
+ if( fileLength > filePointer ) {
+ // There is data to read
+ file.seek( filePointer )
+ while( (line = file.readLine()) != null ) {
+ println '##' + line
+ }
+ filePointer = file.filePointer
+ }
+ // Sleep for the specified interval
+ Thread.sleep( sampleInterval )
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.6
+//----------------------------------------------------------------------------------
+//testfile = newFile('/usr/share/fortune/humorists')
+
+// small files:
+random = new Random()
+lines = testfile.readLines()
+println lines[random.nextInt(lines.size())]
+
+// streamed alternative
+count = 0
+def adage
+testfile.eachLine{ line ->
+ count++
+ if (random.nextInt(count) < 1) adage = line
+}
+println adage
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.7
+//----------------------------------------------------------------------------------
+// non-streamed solution (like Perl and Ruby)
+lines = testfile.readLines()
+Collections.shuffle(lines)
+println lines.join('\n')
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.8
+//----------------------------------------------------------------------------------
+desiredLine = 235
+// for small files
+lines = testfile.readLines()
+println "Line $desiredLine: ${lines[desiredLine-1]}"
+
+// streaming solution
+reader = testfile.newReader()
+count = 0
+def line
+while ((line = reader.readLine())!= null) {
+ if (++count == desiredLine) break
+}
+println "Line $desiredLine: $line"
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.9
+//----------------------------------------------------------------------------------
+println testfile.text.split(/@@pleac@@_8./i).size()
+// => 23 (21 sections .0 .. .20 plus before .0 plus line above)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.10
+//----------------------------------------------------------------------------------
+file = new RandomAccessFile( logfile, "rw" )
+long previous, lastpos = 0
+while( (line = file.readLine()) != null ) {
+ previous = lastpos
+ lastpos = file.filePointer
+}
+if (previous) file.setLength(previous)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.11
+//----------------------------------------------------------------------------------
+// Java's streams are binary at the lowest level if not processed with
+// higher level stream mechanisms or readers/writers. Some additions
+// to the Perl cookbook which illustrate the basics.
+
+// Print first ten bytes of a binary file:
+def dumpStart(filename) {
+ bytes = new File(filename).newInputStream()
+ 10.times{
+ print bytes.read() + ' '
+ }
+ println()
+}
+dumpStart(System.getProperty('java.home')+'/lib/rt.jar')
+// => 80 75 3 4 10 0 0 0 0 0 (note first two bytes = PK - you might recognize this
+// as the starting sequence of a zip file)
+dumpStart('Pleac/classes/pleac8.class') // after running groovyc compiler in src directory
+// => 202 254 186 190 0 0 0 47 2 20 (starting bytes in HEX: CAFEBABE)
+
+binfile = new File('Pleac/data/temp.bin')
+binfile.withOutputStream{ stream -> (0..<20).each{ stream.write(it) }}
+binfile.eachByte{ print it + ' ' }; println()
+// => 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.12
+//----------------------------------------------------------------------------------
+// lets treat binfile as having 5 records of size 4, let's print out the 3rd record
+recsize = 4
+recno = 2 // index starts at 0
+address = recsize * recno
+randomaccess = new RandomAccessFile(binfile, 'r')
+randomaccess.seek(address)
+recsize.times{ print randomaccess.read() + ' ' }; println() // => 8 9 10 11
+randomaccess.close()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.13
+//----------------------------------------------------------------------------------
+// let's take the example from 8.12 but replace the 3rd record with
+// 90 - the original value in the file
+// this is an alternative example to the Perl cookbook which is cross platform
+// see chapter 1 regarding un/pack which could be combined with below
+// to achieve the full functionality of the original 8.13
+recsize = 4
+recno = 2 // index starts at 0
+address = recsize * recno
+randomaccess = new RandomAccessFile(binfile, 'rw')
+randomaccess.seek(address)
+bytes = []
+recsize.times{ bytes += randomaccess.read() }
+randomaccess.seek(address)
+bytes.each{ b -> randomaccess.write(90 - b) }
+randomaccess.close()
+binfile.eachByte{ print it + ' ' }; println()
+// => 0 1 2 3 4 5 6 7 82 81 80 79 12 13 14 15 16 17 18 19
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.14
+//----------------------------------------------------------------------------------
+// reading a String would involve looping and collecting the read bytes
+
+// simple bgets
+// this is similar to the revised 8.13 but would look for the terminating 0
+
+// simplistic strings functionality
+binfile.eachByte{ b -> if ((int)b in 32..126) print ((char)b) }; println() // => RQPO
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.15
+//----------------------------------------------------------------------------------
+// You could combine the byte-level reading/writing mechanisms shown
+// in 8.11 - 8.12 and combine that with the un/pack functionality from
+// Chapter 1 to achieve the desired functionality. A more Java and Groovy
+// friendly way to do this would be to use the Scattering and Gathering
+// stream operations of channels for byte-oriented record fields or
+// data-oriented records. Alternatively, the dataInput/output stream
+// capabilities for data-oriented records. Finally, the
+// objectInput/output stream capabilities could be used for object types.
+// Note, these examples mix reading and writing even though the original
+// Perl example was just about reading.
+
+
+// fixed-length byte-oriented records using channels
+// typical approach used with low-level protocols or file formats
+import java.nio.*
+binfile.delete(); binfile.createNewFile() // start from scratch
+buf1 = ByteBuffer.wrap([10,11,12,13] as byte[]) // simulate 4 byte field
+buf2 = ByteBuffer.wrap([44,45] as byte[]) // 2 byte field
+buf3 = ByteBuffer.wrap('Hello'.bytes) // String
+records = [buf1, buf2, buf3] as ByteBuffer[]
+channel = new FileOutputStream(binfile).channel
+channel.write(records) // gathering byte records
+channel.close()
+binfile.eachByte{ print it + ' ' }; println()
+// => 10 11 12 13 44 45 72 101 108 108 111
+// ScatteringInputStream would convert this back into an array of byte[]
+
+
+// data-oriented streams using channels
+binfile.delete(); binfile.createNewFile() // start from scratch
+buf = ByteBuffer.allocate(24)
+now = System.currentTimeMillis()
+buf.put('PI='.bytes).putDouble(Math.PI).put('Date='.bytes).putLong(now)
+buf.flip() // readies for writing: set length and point back to start
+channel = new FileOutputStream(binfile).channel
+channel.write(buf)
+channel.close()
+// now read it back in
+channel = new FileInputStream(binfile).channel
+buf = ByteBuffer.allocate(24)
+channel.read(buf)
+buf.flip()
+3.times{ print ((char)buf.get()) }
+println (buf.getDouble())
+5.times{ print ((char)buf.get()) }
+println (new Date(buf.getLong()))
+channel.close()
+// =>
+// PI=3.141592653589793
+// Date=Sat Jan 13 00:14:50 EST 2007
+
+// object-oriented streams
+binfile.delete(); binfile.createNewFile() // start from scratch
+class Person implements Serializable { def name, age }
+binfile.withObjectOutputStream{ oos ->
+ oos.writeObject(new Person(name:'Bernie',age:16))
+ oos.writeObject([1:'a', 2:'b'])
+ oos.writeObject(new Date())
+}
+// now read it back in
+binfile.withObjectInputStream{ ois ->
+ person = ois.readObject()
+ println "$person.name is $person.age"
+ println ois.readObject()
+ println ois.readObject()
+}
+// =>
+// Bernie is 16
+// [1:"a", 2:"b"]
+// Sat Jan 13 00:22:13 EST 2007
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.16
+//----------------------------------------------------------------------------------
+// use built-in Java property class
+// suppose you have the following file:
+// # set your database settings here
+// server=localhost
+// url=jdbc:derby:derbyDB;create=true
+// user.name=me
+// user.password=secret
+props = new Properties()
+propsfile=new File('Pleac/data/plain.properties')
+props.load(propsfile.newInputStream())
+props.list(System.out)
+// =>
+// -- listing properties --
+// user.name=me
+// user.password=secret
+// url=jdbc:derby:derbyDB;create=true
+// server=localhost
+
+// There are also provisions for writing properties file.
+
+// (additional example to Perl)
+// You can also read and write xml properties files.
+new File('Pleac/data/props.xml').withOutputStream{ os ->
+ props.storeToXML(os, "Database Settings")
+}
+// =>
+// <?xml version="1.0" encoding="UTF-8"?>
+// <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+// <properties>
+// <comment>Database Settings</comment>
+// <entry key="user.password">secret</entry>
+// <entry key="user.name">me</entry>
+// <entry key="url">jdbc:derby:derbyDB;create=true</entry>
+// <entry key="server">localhost</entry>
+// </properties>
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.17
+//----------------------------------------------------------------------------------
+// The File class provides canRead(), canWrite() and canExecute() (JDK6) methods
+// for finding out about security information specific to the user. JSR 203
+// (expected in Java 7) provides access to additional security related attributes.
+
+// Another useful package to use when wondering about the trustworthiness of a
+// file is the java.security package. It contains many classes. Just one is
+// MessageDigest. This would allow you to create a strong checksum of a file.
+// Your program could refuse to operate if a file it was accessing didn't have the
+// checksum it was expecting - an indication that it may have been tampered with.
+
+// (additional info)
+// While getting file-based security permissions correct is important, it isn't the
+// only mechanism to use for security when using Java based systems. Java provides
+// policy files and an authorization and authentication API which lets you secure
+// any reources (not just files) at various levels of granularity with various
+// security mechanisms.
+// Security policies may be universal, apply to a particular codebase, or
+// using JAAS apply to individuals. Some indicative policy statements:
+// grant {
+// permission java.net.SocketPermission "*", "connect";
+// permission java.io.FilePermission "C:\\users\\cathy\\foo.bat", "read";
+// };
+// grant codebase "file:./*", Principal ExamplePrincipal "Secret" {
+// permission java.io.FilePermission "dummy.txt", "read";
+// };
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.18
+//----------------------------------------------------------------------------------
+// general purpose utility methods
+def getString(buf,size){
+ // consider get(buf[]) instead of get(buf) for efficiency
+ b=[]; size.times{b+=buf.get()}; new String(b as byte[]).trim()
+}
+def getInt(buf,size) {
+ // normally in Java we would just use methods like getLong()
+ // to read a long but wish to ignore platform issues here
+ long val = 0
+ for (n in 0..<size) { val += ((int)buf.get() & 0xFF) << (n * 8) }
+ return val
+}
+def getDate(buf) {
+ return new Date(getInt(buf,4) * 1000) // Java uses millis
+}
+
+// specific utility method (wtmp file from ubuntu 6.10)
+def processWtmpRecords(file, origpos) {
+ channel = new RandomAccessFile(file, 'r').channel
+ recsize = 4 + 4 + 32 + 4 + 32 + 256 + 8 + 4 + 40
+ channel.position(origpos)
+ newpos = origpos
+ buf = ByteBuffer.allocate(recsize)
+ while ((count = channel.read(buf)) != -1) {
+ if (count != recsize) break
+ buf.flip()
+ print getInt(buf,4) + ' ' // type
+ print getInt(buf,4) + ' ' // pid
+ print getString(buf,32) + ' ' // line
+ print getString(buf,4) + ' ' // inittab
+ print getString(buf,32) + ' ' // user
+ print getString(buf,256) + ' ' // hostname
+ buf.position(buf.position() + 8) // skip
+ println "${getDate(buf)} " // time
+ buf.clear()
+ newpos = channel.position()
+ }
+ return newpos
+}
+
+wtmp = new File('Pleac/data/wtmp')
+// wtmpTailingScript:
+sampleInterval = 2000 // 2000 millis = 2 secs
+filePointer = wtmp.size() // begin tailing from the end of the file
+while(true) {
+ // Compare the length of the file to the file pointer
+ long fileLength = wtmp.size()
+ if( fileLength > filePointer ) {
+ // There is data to read
+ filePointer = processWtmpRecords(wtmp, filePointer)
+ }
+ // Sleep for the specified interval
+ Thread.sleep( sampleInterval )
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.19
+//----------------------------------------------------------------------------------
+// contains most of the functionality of the original (not guaranteed to be perfect)
+// -i ignores errors, e.g. if one target is write protected, the others will work
+// -u writes files in unbuffered mode (ignore for '|')
+// -n not to stdout
+// -a all files are in append mode
+// '>>file1' turn on append for individual file
+// '|wc' or '|grep x' etc sends output to forked process (only one at any time)
+class MultiStream {
+ private targets
+ private ignoreErrors
+ MultiStream(List targets, ignore) {
+ this.targets = targets
+ ignoreErrors = ignore
+ }
+ def println(String content) {
+ targets.each{
+ try {
+ it?.write(content.bytes)
+ } catch (Exception ex) {
+ if (!ignoreErrors) throw ex
+ targets -= it
+ it?.close()
+ }
+ }
+ }
+ def close() { targets.each{ it?.close() } }
+}
+
+class TeeTarget {
+ private filename
+ private stream
+ private p
+
+ TeeTarget(String name, append, buffered, ignore) {
+ if (name.startsWith('>>')) {
+ createFileStream(name[2..-1],true,buffered,ignore)
+ } else if (name.startsWith('|')) {
+ createProcessReader(name[1..-1])
+ } else {
+ createFileStream(name,append,buffered,ignore)
+ }
+ }
+
+ TeeTarget(OutputStream stream) { this.stream = stream }
+
+ def write(bytes) { stream?.write(bytes) }
+ def close() { stream?.close() }
+
+ private createFileStream(name, append, buffered, ignore) {
+ filename = name
+ def fos
+ try {
+ fos = new FileOutputStream(name, append)
+ } catch (Exception ex) {
+ if (ignore) return
+ }
+ if (!buffered) stream = fos
+ else stream = new BufferedOutputStream(fos)
+ }
+ private createWriter(os) {new PrintWriter(new BufferedOutputStream(os))}
+ private createReader(is) {new BufferedReader(new InputStreamReader(is))}
+ private createPiperThread(br, pw) {
+ Thread.start{
+ def next
+ while((next = br.readLine())!=null) {
+ pw.println(next)
+ }
+ pw.flush(); pw.close()
+ }
+ }
+ private createProcessReader(name) {
+ def readFromStream = new PipedInputStream()
+ def r1 = createReader(readFromStream)
+ stream = new BufferedOutputStream(new PipedOutputStream(readFromStream))
+ p = Runtime.runtime.exec(name)
+ def w1 = createWriter(p.outputStream)
+ createPiperThread(r1, w1)
+ def w2 = createWriter(System.out)
+ def r2 = createReader(p.inputStream)
+ createPiperThread(r2, w2)
+ }
+}
+
+targets = []
+append = false; ignore = false; includeStdout = true; buffer = true
+(0..<args.size()).each{
+ arg = args[it]
+ if (arg.startsWith('-')) {
+ switch (arg) {
+ case '-a': append = true; break
+ case '-i': ignore = true; break
+ case '-n': includeStdout = false; break
+ case '-u': buffer = false; break
+ default:
+ println "usage: tee [-ainu] [filenames] ..."
+ System.exit(1)
+ }
+ } else targets += arg
+}
+targets = targets.collect{ new TeeTarget(it, append, buffer, ignore) }
+if (includeStdout) targets += new TeeTarget(System.out)
+def tee = new MultiStream(targets, ignore)
+while (line = System.in.readLine()) {
+ tee.println(line)
+}
+tee.close()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_8.20
+//----------------------------------------------------------------------------------
+// most of the functionality - uses an explicit uid - ran on ubuntu 6.10 on intel
+lastlog = new File('Pleac/data/lastlog')
+channel = new RandomAccessFile(lastlog, 'r').channel
+uid = 1000
+recsize = 4 + 32 + 256
+channel.position(uid * recsize)
+buf = ByteBuffer.allocate(recsize)
+channel.read(buf)
+buf.flip()
+date = getDate(buf)
+line = getString(buf,32)
+host = getString(buf,256)
+println "User with uid $uid last logged on $date from ${host?host:'unknown'} on $line"
+// => User with uid 1000 last logged on Sat Jan 13 09:09:35 EST 2007 from unknown on :0
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_9.0
+//----------------------------------------------------------------------------------
+// Groovy builds on Java's file and io classes which provide an operating
+// system independent abstraction of a file system. The actual File class
+// is the main class of interest. It represents a potential file or
+// directory - which may or may not (yet) exist. In versions of Java up to
+// and including Java 6, the File class was missing some of the functionality
+// required to implement some of the examples in the Chapter (workarounds
+// and alternatives are noted below). In Java 7, (also known as "Dolphin")
+// new File abstraction facilities are being worked on but haven't yet been
+// publically released. These new features are known as JSR 203 and are
+// referred to when relevant to some of the examples. Thanks to Alan Bateman
+// from Sun for clarification regarding various aspects of JSR 203. Apologies
+// if I misunderstood any aspects relayed to me and also usual disclaimers
+// apply regarding features which may change or be dropped before release.
+
+// path='/usr/bin'; file='vi' // linux/mac os?
+path='C:/windows'; file='explorer.exe' // windows
+entry = new File("$path")
+assert entry.isDirectory()
+entry = new File("$path/$file")
+assert entry.isFile()
+
+println File.separator
+// => \ (on Windows)
+// => / (on Unix)
+// however if you just stick to backslashes Java converts for you
+// in most situations
+
+// File modification time (no exact equivalent of ctime - but you can
+// call stat() using JNI or use exec() of dir or ls to get this kind of info)
+// JSR 203 also plans to provide such info in Java 7.
+println new Date(entry.lastModified())
+// => Wed Aug 04 07:00:00 EST 2004
+
+// file size
+println entry.size()
+// => 1032192
+
+// check if we have permission to read the file
+assert entry.canRead()
+
+// check if file is binary or text?
+// There is no functionality for this at the file level.
+// Java has the Java Activation Framework (jaf) which is used to
+// associate files (and streams) with MIME Types and subsequently
+// binary data streams or character encodings for (potentially
+// multilanguage) text files. JSR-203 provides a method to determine
+// the MIME type of a file. Depending on the platform the file type may
+// be determined based on a file attribute, file name "extension", the
+// bytes of the files (byte sniffing) or other means. It is service
+// provider based so developers can plug in their own file type detection
+// mechanisms as required. "Out of the box" it will ship with file type
+// detectors that are appropriate for the platform (integrates with GNOME,
+// Windows registry, etc.).
+
+// Groovy uses File for directories and files
+// displayAllFilesInUsrBin:
+new File('/usr/bin').eachFile{ file ->
+ println "Inside /usr/bin is something called $file.name"
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_9.1
+//----------------------------------------------------------------------------------
+file = new File("filename")
+file << 'hi'
+timeModified = file.lastModified()
+println new Date(timeModified)
+// => Sun Jan 07 11:49:02 EST 2007
+
+MILLIS_PER_WEEK = 60 * 60 * 24 * 1000 * 7
+file.setLastModified(timeModified - MILLIS_PER_WEEK)
+println new Date(file.lastModified())
+// => Sun Dec 31 11:49:02 EST 2006
+
+// Java currently doesn't provide access to other timestamps but
+// there are things that can be done:
+// (1) You can use JNI to call to C, e.g. stat()
+// (2) Use exec() and call another program, e.g. dir, ls, ... to get the value you are after
+// (3) Here is a Windows specific patch to get lastAccessedTime and creationTime
+// http://forum.java.sun.com/thread.jspa?forumID=31&start=0&threadID=409921&range=100#1800193
+// (4) There is an informal patch for Java 5/6 which gives lastAccessedTime on Windows and Linux
+// and creationTime on windows:
+// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6314708
+// (5) JSR 203 (currently targetted for Java 7) aims to provide
+// "bulk access to file attributes, change notification, escape to filesystem-specific APIs"
+// this is supposed to include creationTime and lastAccessedTime along with many
+// security-related file attributes
+
+// viFileWithoutChangingModificationTimeScript:
+#!/usr/bin/groovy
+// uvi - vi a file without changing it's last modified time
+if (args.size() != 1)
+ println "usage: uvi filename"
+ System.exit(1)
+}
+file = args[0]
+origTime = new File(file).lastModified()
+"vi $file".execute()
+new File(file).setLastModified(origTime)
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_9.2
+//----------------------------------------------------------------------------------
+println new File('/doesnotexist').exists() // => false
+println new File('/doesnotexist').delete() // => false
+
+new File('/createme') << 'Hi there'
+println new File('/createme').exists() // => true
+println new File('/createme').delete() // => true
+
+names = ['file1','file2','file3']
+files = names.collect{ new File(it) }
+// create 2 of the files
+files[0..1].each{ f -> f << f.name }
+
+def deleteFiles(files) {
+ def problemFileNames = []
+ files.each{ f ->
+ if (!f.delete())
+ problemFileNames += f.name
+ }
+ def delCnt = files.size() - problemFileNames.size()
+ println "Successfully deleted $delCnt of ${files.size()} file(s)"
+ if (problemFileNames)
+ println "Problems file(s): " + problemFileNames.join(', ')
+}
+
+deleteFiles(files)
+// =>
+// Successfully deleted 2 of 3 file(s)
+// Problems file(s): file3
+
+// we can also set files for deletion on exit
+tempFile = new File('/xxx')
+assert !tempFile.exists()
+tempFile << 'junk'
+assert tempFile.exists()
+tempFile.deleteOnExit()
+assert tempFile.exists()
+// To confirm this is working, run these steps multiple times in a row.
+
+// Discussion:
+// Be careful with deleteOnExit() as there is no way to cancel it.
+// There are also mechanisms specifically for creating unqiuely named temp files.
+// On completion of JSR 203, there will be additional methods available for
+// deleting which throw exceptions with detailed error messages rather than
+// just return booleans.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_9.3
+//----------------------------------------------------------------------------------
+// (1) Copy examples
+
+//shared setup
+dummyContent = 'some content' + System.getProperty('line.separator')
+setUpFromFile()
+setUpToFile()
+
+// built-in copy via memory (text files only)
+to << from.text
+checkSuccessfulCopyAndDelete()
+
+// built-in as a stream (text or binary) with optional encoding
+to << from.asWritable('US-ASCII')
+checkSuccessfulCopyAndDelete()
+
+// built-in using AntBuilder
+// for options, see: http://ant.apache.org/manual/CoreTasks/copy.html
+new AntBuilder().copy( file: from.canonicalPath, tofile: to.canonicalPath )
+checkSuccessfulCopyAndDelete()
+// =>
+// [copy] Copying 1 file to D:\
+
+
+// use Apache Jakarta Commons IO (jakarta.apache.org)
+import org.apache.commons.io.FileUtils
+// Copies a file to a new location preserving the lastModified date.
+FileUtils.copyFile(from, to)
+checkSuccessfulCopyAndDelete()
+
+// using execute()
+// "cp $from.canonicalPath $to.canonicalPath".execute() // unix
+println "cmd /c \"copy $from.canonicalPath $to.canonicalPath\"".execute().text // dos vms
+checkSuccessfulCopyAndDelete()
+// =>
+// 1 file(s) copied.
+
+// (2) Move examples
+// You can just do copy followed by delete but many OS's can just 'rename' in place
+// so you can additionally do using Java's functionality:
+assert from.renameTo(to)
+assert !from.exists()
+checkSuccessfulCopyAndDelete()
+// whether renameTo succeeds if from and to are on different platforms
+// or if to pre-exists is OS dependent, so you should check the return boolean
+
+// alternatively, Ant has a move task:
+// http://ant.apache.org/manual/CoreTasks/move.html
+
+//helper methods
+def checkSuccessfulCopyAndDelete() {
+ assert to.text == dummyContent
+ assert to.delete()
+ assert !to.exists()
+}
+def setUpFromFile() {
+ from = new File('/from.txt') // just a name
+ from << dummyContent // now its a real file with content
+ from.deleteOnExit() // that will be deleted on exit
+}
+def setUpToFile() {
+ to = new File('C:/to.txt') // target name
+ to.delete() // ensure not left from previous aborted run
+ assert !to.exists() // double check
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_9.4
+//----------------------------------------------------------------------------------
+// Groovy (because of its Java heritage) doesn't have an exact
+// equivalent of stat - as per 9.2 there are numerous mechanisms
+// to achieve the equivalent, in particular, JSR203 (still in draft)
+// has specific SymLink support including a FileId class in the
+// java.nio.filesystems package. This will allow (depending on the
+// operating system capabilities) files to be uniquely identified.
+// If you work on Unix or Linux then you'll recognize this as it device/inode.
+
+// If you are not interested in the above workarounds/future features
+// and you are on a unix system, you can compare the absolutePath and
+// canonicalPath attributes for a file. If they are different it is
+// a symbolic link. On other operating systems, this difference is not
+// to be relied upon and even on *nix systems, this will only get you
+// so far and will also be relatively expensive resource and timewise.
+
+// process only unique files
+seen = []
+def myProcessing(file) {
+ def path = file.canonicalPath
+ if (!seen.contains(path)) {
+ seen << path
+ // do something with file because we haven't seen it before
+ }
+}
+
+// find linked files
+seen = [:]
+filenames = ['/dummyfile1.txt','/test.lnk','/dummyfile2.txt']
+filenames.each{ filename ->
+ def file = new File(filename)
+ def cpath = file.canonicalPath
+ if (!seen.containsKey(cpath)) {
+ seen[cpath] = []
+ }
+ seen[cpath] += file.absolutePath
+}
+
+println 'Files with links:'
+println seen.findAll{ k,v -> v.size() > 1 }
+//---------------------------------------------------------------------------------
+
+// @@PLEAC@@_9.5
+//----------------------------------------------------------------------------------
+// general pattern is:
+// new File('dirname').eachFile{ /* do something ... */ }
+
+// setup (change this on your system)
+basedir = 'Pleac/src'
+
+// process all files printing out full name (. and .. auto excluded)
+new File(basedir).eachFile{ f->
+ if (f.isFile()) println f.canonicalPath
+}
+// also remove dot files such as '.svn' and '.cvs' etc.
+new File(basedir).eachFileMatch(~'^[^.].*'){ f->
+ if (f.isFile()) println f.canonicalPath
+}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_9.6
+//----------------------------------------------------------------------------------
+// Globbing via Apache Jakarta ORO
+import org.apache.oro.io.GlobFilenameFilter
+dir = new File(basedir)
+namelist = dir.list(new GlobFilenameFilter('*.c'))
+filelist = dir.listFiles(new GlobFilenameFilter('*.h') as FilenameFilter)
+
+// Built-in matching using regex's
+files = []
+new File(basedir).eachFileMatch(~/\.[ch]$/){ f->
+ if (f.isFile()) files += f
+}
+
+// Using Ant's FileScanner (supports arbitrary nested levels using **)
+// For more details about Ant FileSets, see here:
+// http://ant.apache.org/manual/CoreTypes/fileset.html
+scanner = new AntBuilder().fileScanner {
+ fileset(dir:basedir) {
+ include(name:'**/pleac*.groovy')
+ include(name:'Slowcat.*y')
+ exclude(name:'**/pleac??.groovy') // chaps 10 and above
+ exclude(name:'**/*Test*', unless:'testMode')
+ }
+}
+for (f in scanner) {
+ println "Found file $f"
+}
+
+// find and sort directories with numeric names
+candidateFiles = new File(basedir).listFiles()
+allDigits = { it.name =~ /^\d+$/ }
+isDir = { it.isDirectory() }
+dirs = candidateFiles.findAll(isDir).findAll(allDigits)*.canonicalPath.sort()
+println dirs
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_9.7
+//----------------------------------------------------------------------------------
+// find all files recursively
+dir = new File(basedir)
+files = []
+dir.eachFileRecurse{ files += it }
+
+// find total size
+sum = files.sum{ it.size() }
+println "$basedir contains $sum bytes"
+// => Pleac/src contains 365676 bytes
+
+// find biggest
+biggest = files.max{ it.size() }
+println "Biggest file is $biggest.name with ${biggest.size()} bytes"
+// => Biggest file is pleac6.groovy with 42415 bytes
+
+// find most recently modified
+youngest = files.max{ it.lastModified() }
+println "Most recently modified is $youngest.name, changed ${new Date(youngest.lastModified())}"
+// => Most recently modified is pleac9.groovy, changed Tue Jan 09 07:35:39 EST 2007
+
+// find all directories
+dir.eachDir{ println 'Found: ' + it.name}
+
+// find all directories recursively
+dir.eachFileRecurse{ f -> if (f.isDirectory()) println 'Found: ' + f.canonicalPath}
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_9.8
+//----------------------------------------------------------------------------------
+base = new File('path_to_somewhere_to_delete')
+
+// delete using Jakarta Apache Commons IO
+FileUtils.deleteDirectory(base)
+
+// delete using Ant, for various options see:
+// http://ant.apache.org/manual/CoreTasks/delete.html
+ant = new AntBuilder()
+ant.delete(dir: base)
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_9.9
+//----------------------------------------------------------------------------------
+names = ['Pleac/src/abc.java', 'Pleac/src/def.groovy']
+names.each{ name -> new File(name).renameTo(new File(name + '.bak')) }
+
+// The Groovy way of doing rename using an expr would be to use a closure
+// for the expr:
+// groovySimpleRenameScript:
+#!/usr/bin/groovy
+// usage rename closure_expr filenames
+op = args[0]
+println op
+files = args[1..-1]
+shell = new GroovyShell(binding)
+files.each{ f ->
+ newname = shell.evaluate("$op('$f')")
+ new File(f).renameTo(new File(newname))
+}
+
+// this would allow processing such as:
+//% rename "{n -> 'FILE_' + n.toUpperCase()}" files
+// with param pleac9.groovy => FILE_PLEAC9.GROOVY
+//% rename "{n -> n.replaceAll(/9/,'nine') }" files
+// with param pleac9.groovy => pleacnine.groovy
+// The script could also be modified to take the list of
+// files from stdin if no args were present (not shown).
+
+// The above lets you type any Groovy code, but instead you might
+// decide to provide the user with some DSL-like additions, e.g.
+// adding the following lines into the script:
+sep = File.separator
+ext = { '.' + it.tokenize('.')[-1] }
+base = { new File(it).name - ext(it) }
+parent = { new File(it).parent }
+lastModified = { new Date(new File(it).lastModified()) }
+// would then allow the following more succinct expressions:
+//% rename "{ n -> parent(n) + sep + base(n).reverse() + ext(n) }" files
+// with param Pleac/src/pleac9.groovy => Pleac\src\9caelp.groovy
+//% rename "{ n -> base(n) + '_' + lastModified(n).year + ext(n) }" files
+// with param pleac9.groovy => pleac9_07.groovy
+
+// As a different alternative, you could hook into Ant's mapper mechanism.
+// You wouldn't normally type in this from the command-line but it could
+// be part of a script, here is an example (excludes the actual rename part)
+ant = new AntBuilder()
+ant.pathconvert(property:'result',targetos:'windows'){
+ path(){ fileset(dir:'Pleac/src', includes:'pleac?.groovy') }
+ compositemapper{
+ globmapper(from:'*1.groovy', to:'*1.groovy.bak')
+ regexpmapper(from:/^(.*C2)\.(.*)$/, to:/\1_beta.\2/, casesensitive:'no')
+ chainedmapper{
+ packagemapper(from:'*pleac3.groovy', to:'*3.xml')
+ filtermapper(){ replacestring(from:'C:.', to:'') }
+ }
+ chainedmapper{
+ regexpmapper(from:/^(.*)4\.(.*)$/, to:/\1_4.\2/)
+ flattenmapper()
+ filtermapper(){ replacestring(from:'4', to:'four') }
+ }
+ }
+}
+println ant.antProject.getProperty('result').replaceAll(';','\n')
+// =>
+// C:\Projects\GroovyExamples\Pleac\src\pleac1.groovy.bak
+// C:\Projects\GroovyExamples\Pleac\src\pleac2_beta.groovy
+// Projects.GroovyExamples.Pleac.src.3.xml
+// pleac_four.groovy
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_9.10
+//----------------------------------------------------------------------------------
+// Splitting a Filename into Its Component Parts
+path = new File('Pleac/src/pleac9.groovy')
+assert path.parent == 'Pleac' + File.separator + 'src'
+assert path.name == 'pleac9.groovy'
+ext = path.name.tokenize('.')[-1]
+assert ext == 'groovy'
+
+// No fileparse_set_fstype() equivalent in Groovy/Java. Java's File constructor
+// automatically performs such a parse and does so appropriately of the operating
+// system it is running on. In addition, 3rd party libraries allow platform
+// specific operations ot be performed. As an example, many Ant tasks are OS
+// aware, e.g. the pathconvert task (callable from an AntBuilder instance) has
+// a 'targetos' parameter which can be one of 'unix', 'windows', 'netware',
+// 'tandem' or 'os/2'.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_9.11
+//----------------------------------------------------------------------------------
+// Given the previous discussion regarding the lack of support for symlinks
+// in Java's File class without exec'ing to the operating system or doing
+// a JNI call (at least until JSR 203 arrives), I have modified this example
+// to perform an actual replica forest of actual file copies rather than
+// a shadow forest full of symlinks pointing back at the real files.
+// Use Apache Jakarta Commons IO
+srcdir = new File('Pleac/src') // path to src
+destdir = new File('C:/temp') // path to dest
+preserveFileStamps = true
+FileUtils.copyDirectory(srcdir, destdir, preserveFileStamps)
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_9.12
+//----------------------------------------------------------------------------------
+#!/usr/bin/groovy
+// lst - list sorted directory contents (depth first)
+// Given the previous discussion around Java's more limited Date
+// information available via the File class, this will be a reduced
+// functionality version of ls
+LONG_OPTION = 'l'
+REVERSE_OPTION = 'r'
+MODIFY_OPTION = 'm'
+SIZE_OPTION = 's'
+HELP_OPTION = 'help'
+
+op = new joptsimple.OptionParser()
+op.accepts( LONG_OPTION, 'long listing' )
+op.accepts( REVERSE_OPTION, 'reverse listing' )
+op.accepts( MODIFY_OPTION, 'sort based on modification time' )
+op.accepts( SIZE_OPTION, 'sort based on size' )
+op.accepts( HELP_OPTION, 'display this message' )
+
+options = op.parse(args)
+if (options.wasDetected( HELP_OPTION )) {
+ op.printHelpOn( System.out )
+} else {
+ sort = {}
+ params = options.nonOptionArguments()
+ longFormat = options.wasDetected( LONG_OPTION )
+ reversed = options.wasDetected( REVERSE_OPTION )
+ if (options.wasDetected( SIZE_OPTION )) {
+ sort = {a,b -> a.size()<=>b.size()}
+ } else if (options.wasDetected( MODIFY_OPTION )) {
+ sort = {a,b -> a.lastModified()<=>b.lastModified()}
+ }
+ displayFiles(params, longFormat, reversed, sort)
+}
+
+def displayFiles(params, longFormat, reversed, sort) {
+ files = []
+ params.each{ name -> new File(name).eachFileRecurse{ files += it } }
+ files.sort(sort)
+ if (reversed) files = files.reverse()
+ files.each { file ->
+ if (longFormat) {
+ print (file.directory ? 'd' : '-' )
+ print (file.canRead() ? 'r' : '-' )
+ print (file.canWrite() ? 'w ' : '- ' )
+ //print (file.canExecute() ? 'x' : '-' ) // Java 6
+ print file.size().toString().padLeft(12) + ' '
+ print new Date(file.lastModified()).toString().padRight(22)
+ println ' ' + file
+ } else {
+ println file
+ }
+ }
+}
+
+// =>
+// % lst -help
+// Option Description
+// ------ -------------------------------
+// --help display this message
+// -l long listing
+// -m sort based on modification time
+// -r reverse listing
+// -s sort based on size
+//
+// % lst -l -m Pleac/src Pleac/lib
+// ...
+// drw 0 Mon Jan 08 22:33:00 EST 2007 Pleac\lib\.svn
+// -rw 18988 Mon Jan 08 22:33:41 EST 2007 Pleac\src\pleac9.groovy
+// -rw 2159 Mon Jan 08 23:15:41 EST 2007 Pleac\src\lst.groovy
+//
+// % -l -s -r Pleac/src Pleac/lib
+// -rw 1034049 Sun Jan 07 19:24:41 EST 2007 Pleac\lib\ant.jar
+// -r- 1034049 Sun Jan 07 19:40:27 EST 2007 Pleac\lib\.svn\text-base\ant.jar.svn-base
+// -rw 421008 Thu Jun 02 15:15:34 EST 2005 Pleac\lib\ant-nodeps.jar
+// -rw 294436 Sat Jan 06 21:19:58 EST 2007 Pleac\lib\geronimo-javamail_1.3.1_mail-1.0.jar
+// ...
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_10.0
+//----------------------------------------------------------------------------------
+def hello() {
+ greeted += 1
+ println "hi there!"
+}
+
+// We need to initialize greeted before it can be used, because "+=" assumes predefinition
+greeted = 0
+hello()
+println greeted
+// =>
+// hi there
+// 1
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.1
+//----------------------------------------------------------------------------------
+// basic method calling examples
+// In Groovy, parameters are named anyway
+def hypotenuse(side1, side2) {
+ Math.sqrt(side1**2 + side2**2) // sqrt in Math package
+}
+diag = hypotenuse(3, 4)
+assert diag == 5
+
+// the star operator will magically convert an Array into a "tuple"
+a = [5, 12]
+assert hypotenuse(*a) == 13
+
+// both = men + women
+
+// In Groovy, all objects are references, so the same problem arises.
+// Typically we just return a new object. Especially for immutable objects
+// this style of processing is very common.
+nums = [1.4, 3.5, 6.7]
+def toInteger(n) {
+ n.collect { v -> v.toInteger() }
+}
+assert toInteger(nums) == [1, 3, 6]
+
+orignums = [1.4, 3.5, 6.7]
+def truncMe(n) {
+ (0..<n.size()).each{ idx -> n[idx] = n[idx].toInteger() }
+}
+truncMe(orignums)
+assert orignums == [1, 3, 6]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.2
+//----------------------------------------------------------------------------------
+// variable scope examples
+def somefunc() {
+ def variableInMethod // private is default in a method
+}
+
+def name // private is default for variable in a script
+
+bindingVar = 10 // this will be in the binding (sort of global)
+globalArray = []
+
+// In Groovy, run_check can't access a, b, or c until they are
+// explicitely defined global (using leading $), even if they are
+// both defined in the same scope
+
+def checkAccess(x) {
+ def y = 200
+ return x + y + bindingVar // access private, param, global
+}
+assert checkAccess(7) == 217
+
+def saveArray(ary) {
+ globalArray << 'internal'
+ globalArray += ary
+}
+
+saveArray(['important'])
+assert globalArray == ["internal", "important"]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.3
+//----------------------------------------------------------------------------------
+// you want a private persistent variable within a script method
+
+// you could use a helper class for this
+class CounterHelper {
+ private static counter = 0
+ def static next() { ++counter }
+}
+def greeting(s) {
+ def n = CounterHelper.next()
+ println "Hello $s (I have been called $n times)"
+}
+greeting('tom')
+greeting('dick')
+greeting('harry')
+// =>
+// Hello tom (I have been called 1 times)
+// Hello dick (I have been called 2 times)
+// Hello harry (I have been called 3 times)
+
+// you could make it more fancy by having separate keys,
+// using synchronisation, singleton pattern, ThreadLocal, ...
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_10.4
+//----------------------------------------------------------------------------------
+// Determining Current Method Name
+// Getting class, package and static info is easy. Method info is just a little work.
+// From Java we can use:
+// new Exception().stackTrace[0].methodName
+// or for Java 5 and above (saves relatively expensive exception creation)
+// Thread.currentThread().stackTrace[3].methodName
+// But these give the Java method name. Groovy wraps its own runtime
+// system over the top. It's still a Java method, just a little bit further up the
+// stack from where we might expect. Getting the Groovy method name can be done in
+// an implementation specific way (subject to change as the language evolves):
+def myMethod() {
+ names = new Exception().stackTrace*.methodName
+ println groovyUnwrap(names)
+}
+def myMethod2() {
+ names = Thread.currentThread().stackTrace*.methodName
+ names = names[3..<names.size()] // skip call to dumpThread
+ println groovyUnwrap(names)
+}
+def groovyUnwrap(names) { names[names.indexOf('invoke0')-1] }
+myMethod() // => myMethod
+myMethod2() // => myMethod2
+
+// Discussion: If what you really wanted was a tracing mechanism, you could overrie
+// invokeMethod and print out method names before calling the original method. Or
+// you could use one of the Aspect-Oriented Programming packages for Java.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.5
+//----------------------------------------------------------------------------------
+// Passing Arrays and Hashes by Reference
+// In Groovy, every value is a reference to an object, thus there is
+// no such problem, just call: arrayDiff(array1, array2)
+
+// pairwise add (altered so it doesn't require equal sizes)
+def pairWiseAdd(a1, a2) {
+ s1 = a1.size(); s2 = a2.size()
+ (0..<[s1,s2].max()).collect{
+ it > s1-1 ? a2[it] : (it > s2-1 ? a1[it] : a1[it] + a2[it])
+ }
+}
+a = [1, 2]
+b = [5, 8]
+assert pairWiseAdd(a, b) == [6, 10]
+
+// also works for unequal sizes
+b = [5, 8, -1]
+assert pairWiseAdd(a, b) == [6, 10, -1]
+b = [5]
+assert pairWiseAdd(a, b) == [6, 2]
+
+// We could check if both arguments were of a particular type, e.g.
+// (a1 instanceof List) or (a2.class.isArray()) but duck typing allows
+// it to work on other things as well, so while wouldn't normally do this
+// you do need to be a little careful when calling the method, e.g.
+// here we call it with two maps of strings and get back strings
+// the important thing here was that the arguments were indexed
+// 0..size-1 and that the items supported the '+' operator (as String does)
+a = [0:'Green ', 1:'Grey ']
+b = [0:'Frog', 1:'Elephant', 2:'Dog']
+assert pairWiseAdd(a, b) == ["Green Frog", "Grey Elephant", "Dog"]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.6
+//----------------------------------------------------------------------------------
+// Detecting Return Context
+// There is no exact equivalent of return context in Groovy but
+// you can behave differently when called under different circumstances
+def addValueOrSize(a1, a2) {
+ b1 = (a1 instanceof Number) ? a1 : a1.size()
+ b2 = (a2 instanceof Number) ? a2 : a2.size()
+ b1 + b2
+}
+assert (addValueOrSize(10, 'abcd')) == 14
+assert (addValueOrSize(10, [25, 50])) == 12
+assert (addValueOrSize('abc', [25, 50])) == 5
+assert (addValueOrSize(25, 50)) == 75
+
+// Of course, a key feature of many OO languages including Groovy is
+// method overloading so that responding to dofferent parameters has
+// a formal way of being captured in code with typed methods, e.g.
+class MakeBiggerHelper {
+ def triple(List iList) { iList.collect{ it * 3 } }
+ def triple(int i) { i * 3 }
+}
+mbh = new MakeBiggerHelper()
+assert mbh.triple([4, 5]) == [12, 15]
+assert mbh.triple(4) == 12
+
+// Of course with duck typing, we can rely on dynamic typing if we want
+def directTriple(arg) {
+ (arg instanceof Number) ? arg * 3 : arg.collect{ it * 3 }
+}
+assert directTriple([4, 5]) == [12, 15]
+assert directTriple(4) == 12
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.7
+//----------------------------------------------------------------------------------
+// Passing by Named Parameter
+// Groovy supports named params or positional arguments with optional
+// defaults to simplify method calling
+
+// named arguments work by using a map
+def thefunc(Map args) {
+ // in this example, we just call the positional version
+ thefunc(args.start, args.end, args.step)
+}
+
+// positional arguments with defaults
+def thefunc(start=0, end=30, step=10) {
+ ((start..end).step(step))
+}
+
+assert thefunc() == [0, 10, 20, 30]
+assert thefunc(15) == [15, 25]
+assert thefunc(0,40) == [0, 10, 20, 30, 40]
+assert thefunc(start:5, end:20, step:5) == [5, 10, 15, 20]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.8
+//----------------------------------------------------------------------------------
+// Skipping Selected Return Values
+// Groovy 1.0 doesn't support multiple return types, so you always use
+// a holder class, array or collection to return multiple values.
+def getSystemInfo() {
+ def millis = System.currentTimeMillis()
+ def freemem = Runtime.runtime.freeMemory()
+ def version = System.getProperty('java.vm.version')
+ return [millis:millis, freemem:freemem, version:version]
+ // if you are likely to want all the information use a list
+ // return [millis, freemem, version]
+ // or dedicated holder class
+ // return new SystemInfo(millis, freemem, version)
+}
+result = getSystemInfo()
+println result.version
+// => 1.5.0_08-b03
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.9
+//----------------------------------------------------------------------------------
+// Returning More Than One Array or Hash
+// As per 10.8, Groovy 1.0 doesn't support multiple return types but you
+// just use a holder class, array or collection. There are no limitations
+// on returning arbitrary nested values using this technique.
+def getInfo() {
+ def system = [millis:System.currentTimeMillis(),
+ version:System.getProperty('java.vm.version')]
+ def runtime = [freemem:Runtime.runtime.freeMemory(),
+ maxmem:Runtime.runtime.maxMemory()]
+ return [system:system, runtime:runtime]
+}
+println info.runtime.maxmem // => 66650112 (info automatically calls getInfo() here)
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.10
+//----------------------------------------------------------------------------------
+// Returning Failure
+// This is normally done in a heavy-weight way via Java Exceptions
+// (see 10.12) or in a lightweight way by returning null
+def sizeMinusOne(thing) {
+ if (thing instanceof Number) return
+ thing.size() - 1
+}
+def check(thing) {
+ result = sizeMinusOne(thing)
+ println (result ? "Worked with result: $result" : 'Failed')
+}
+check(4)
+check([1, 2])
+check('abc')
+// =>
+// Failed
+// Worked with result: 1
+// Worked with result: 2
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.11
+//----------------------------------------------------------------------------------
+// Prototyping Functions: Not supported by Groovy but arguably
+// not important given other language features.
+
+// Omitting Parentheses Scenario: Groovy only lets you leave out
+// parentheses in simple cases. If you had two methods sum(a1,a2,a3)
+// and sum(a1,a2), there would be no way to indicate that whether
+// 'sum sum 2, 3, 4, 5' meant sum(sum(2,3),4,5) or sum(sum(2,3,4),5).
+// You would have to include the parentheses. Groovy does much less
+// auto flattening than some other languages; it provides a *args
+// operator, varargs style optional params and supports method
+// overloading and ducktyping. Perhaps these other features mean
+// that this scenario is always easy to avoid.
+def sum(a,b,c){ a+b+c*2 }
+def sum(a,b){ a+b }
+// sum sum 1,2,4,5
+// => compilation error
+sum sum(1,2),4,5
+sum sum(1,2,4),5
+// these work but if you try to do anything fancy you will run into trouble;
+// your best bet is to actually include all the parentheses:
+println sum(sum(1,2),4,5) // => 17
+println sum(sum(1,2,4),5) // => 16
+
+// Mimicking built-ins scenario: this is a mechanism to turn-off
+// auto flattening, Groovy only does flattening in restricted circumstances.
+// func(array, 1, 2, 3) is never coerced into a single list but varargs
+// and optional args can be used instead
+def push(list, Object[] optionals) {
+ optionals.each{ list.add(it) }
+}
+items = [1,2]
+newItems = [7, 8, 9]
+push items, 3, 4
+push items, 6
+push (items, *newItems) // brackets currently required, *=flattening
+ // without *: items = [1, 2, 3, 4, 6, [7, 8, 9]]
+assert items == [1, 2, 3, 4, 6, 7, 8, 9]
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.12
+//----------------------------------------------------------------------------------
+// Handling Exceptions
+// Same story as in Java but Groovy has some nice Checked -> Unchecked
+// magic behind the scenes (Java folk will know what this means)
+// When writing methods:
+// throw exception to raise it
+// When calling methods:
+// try ... catch ... finally surrounds processing logic
+def getSizeMostOfTheTime(s) {
+ if (s =~ 'Full Moon') throw new RuntimeException('The world is ending')
+ s.size()
+}
+try {
+ println 'Size is: ' + getSizeMostOfTheTime('The quick brown fox')
+ println 'Size is: ' + getSizeMostOfTheTime('Beware the Full Moon')
+} catch (Exception ex) {
+ println "Error was: $ex.message"
+} finally {
+ println 'Doing common cleanup'
+}
+// =>
+// Size is: 19
+// Error was: The world is ending
+// Doing common cleanup
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.13
+//----------------------------------------------------------------------------------
+// Saving Global Values
+// We can just save the value and restore it later:
+def printAge() { println "Age is $age" }
+
+age = 18 // binding "global" variable
+printAge() // => 18
+
+if (age > 0) {
+ def origAge = age
+ age = 23
+ printAge() // => 23
+ age = origAge
+}
+printAge() // => 18
+
+// Depending on the circmstances we could enhance this in various ways
+// such as synchronizing, surrounding with try ... finally, using a
+// memento pattern, saving the whole binding, using a ThreadLocal ...
+
+// There is no need to use local() for filehandles or directory
+// handles in Groovy because filehandles are normal objects.
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.14
+//----------------------------------------------------------------------------------
+// Redefining a Function
+// This can be done via a number of ways:
+
+// OO approach:
+// The standard trick using OO is to override methods in subclasses
+class Parent { def foo(){ println 'foo' } }
+class Child extends Parent { def foo(){ println 'bar' } }
+new Parent().foo() // => foo
+new Child().foo() // => bar
+
+// Category approach:
+// If you want to redefine a method from an existing library
+// you can use categories. This can be done to avoid name conflicts
+// or to patch functionality with local mods without changing
+// original code
+println new Date().toString()
+// => Sat Jan 06 16:44:55 EST 2007
+class DateCategory {
+ static toString(Date self) { 'not telling' }
+}
+use (DateCategory) {
+ println new Date().toString()
+}
+// => not telling
+
+// Closure approach:
+// Groovy's closures let you have "anonymous methods" as objects.
+// This allows you to be very flexible with "method" redefinition, e.g.:
+colors = 'red yellow blue green'.split(' ').toList()
+color2html = new Expando()
+colors.each { c ->
+ color2html[c] = { args -> "<FONT COLOR='$c'>$args</FONT>" }
+}
+println color2html.yellow('error')
+// => <FONT COLOR='yellow'>error</FONT>
+color2html.yellow = { args -> "<b>$args</b>" } // too hard to see yellow
+println color2html.yellow('error')
+// => <b>error</b>
+
+// Other approaches:
+// you could use invokeMethod to intercept the original method and call
+// your modified method on just particular input data
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.15
+//----------------------------------------------------------------------------------
+// Trapping Undefined Function Calls
+class FontHelper {
+ // we could define all the important colors explicitly like this
+ def pink(info) {
+ buildFont('hot pink', info)
+ }
+ // but this method will catch any undefined ones
+ def invokeMethod(String name, Object args) {
+ buildFont(name, args.join(' and '))
+ }
+ def buildFont(name, info) {
+ "<FONT COLOR='$name'>" + info + "</FONT>"
+ }
+}
+fh = new FontHelper()
+println fh.pink("panther")
+println fh.chartreuse("stuff", "more stuff")
+// =>
+// <FONT COLOR='hot pink'>panther</FONT>
+// <FONT COLOR='chartreuse'>stuff and more stuff</FONT>
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.16
+//----------------------------------------------------------------------------------
+// Simulating Nested Subroutimes: Using Closures within Methods
+def outer(arg) {
+ def x = arg + 35
+ inner = { x * 19 }
+ x + inner()
+}
+assert outer(10) == 900
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_10.17
+//----------------------------------------------------------------------------------
+// Program: Sorting Your Mail
+#!/usr/bin/groovy
+import javax.mail.*
+
+// solution using mstor package (mstor.sf.net)
+session = Session.getDefaultInstance(new Properties())
+store = session.getStore(new URLName('mstor:/path_to_your_mbox_directory'))
+store.connect()
+
+// read messages from Inbox
+inbox = store.defaultFolder.getFolder('Inbox')
+inbox.open(Folder.READ_ONLY)
+messages = inbox.messages.toList()
+
+// extractor closures
+subject = { m -> m.subject }
+subjectExcludingReplyPrefix = { m -> subject(m).replaceAll(/(?i)Re:\\s*/,'') } // double slash to single outside triple quotes
+date = { m -> d = m.sentDate; new Date(d.year, d.month, d.date) } // ignore time fields
+
+// sort by subject excluding 'Re:' prefixs then print subject for first 6
+println messages.sort{subjectExcludingReplyPrefix(it)}[0..5]*.subject.join('\n')
+// =>
+// Additional Resources for JDeveloper 10g (10.1.3)
+// Amazon Web Services Developer Connection Newsletter #18
+// Re: Ant 1.7.0?
+// ARN Daily | 2007: IT predictions for the year ahead
+// Big Changes at Gentleware
+// BigPond Account Notification
+
+// sort by date then subject (print first 6 entries)
+sorted = messages.sort{ a,b ->
+ date(a) == date(b) ?
+ subjectExcludingReplyPrefix(a) <=> subjectExcludingReplyPrefix(b) :
+ date(a) <=> date(b)
+}
+sorted[0..5].each{ m -> println "$m.sentDate: $m.subject" }
+// =>
+// Wed Jan 03 08:54:15 EST 2007: ARN Daily | 2007: IT predictions for the year ahead
+// Wed Jan 03 15:33:31 EST 2007: EclipseSource: RCP Adoption, Where Art Thou?
+// Wed Jan 03 00:10:11 EST 2007: What's New at Sams Publishing?
+// Fri Jan 05 08:31:11 EST 2007: Building a Sustainable Open Source Business
+// Fri Jan 05 09:53:45 EST 2007: Call for Participation: Agile 2007
+// Fri Jan 05 05:51:36 EST 2007: IBM developerWorks Weekly Edition, 4 January 2007
+
+// group by date then print first 2 entries of first 2 dates
+groups = messages.groupBy{ date(it) }
+groups.keySet().toList()[0..1].each{
+ println it
+ println groups[it][0..1].collect{ ' ' + it.subject }.join('\n')
+}
+// =>
+// Wed Jan 03 00:00:00 EST 2007
+// ARN Daily | 2007: IT predictions for the year ahead
+// EclipseSource: RCP Adoption, Where Art Thou?
+// Fri Jan 05 00:00:00 EST 2007
+// Building a Sustainable Open Source Business
+// Call for Participation: Agile 2007
+
+
+// @@PLEAC@@_11.0
+//----------------------------------------------------------------------------------
+// In Groovy, most usages of names are references (there are some special
+// rules for the map shorthand notation and builders).
+// Objects are inherently anonymous, they don't know what names refer to them.
+ref = 3 // points ref to an Integer object with value 3.
+println ref // prints the value that the name ref refers to.
+
+myList = [3, 4, 5] // myList is a name for this list
+anotherRef = myList
+myMap = ["How": "Now", "Brown": "Cow"] // myMap is a name for this map
+
+anArray = [1, 2, 3] as int[] // creates an array of three references to Integer objects
+
+list = [[]] // a list containing an empty list
+list[2] = 'Cat'
+println list // => [[], null, "Cat"]
+list[0][2] = 'Dog'
+println list // => [[null, null, "Dog"], null, "Cat"]
+
+a = [2, 1]
+b = a // b is a reference to the same thing as a
+a.sort()
+println b // => [1, 2]
+
+nat = [ Name: "Leonhard Euler",
+ Address: "1729 Ramanujan Lane\nMathworld, PI 31416",
+ Birthday: 0x5bb5580
+]
+println nat
+// =>["Address":"1729 Ramanujan Lane\nMathworld, PI 31416", "Name":"Leonhard Euler", "Birthday":96163200]
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.1
+//----------------------------------------------------------------------------------
+aref = myList
+anonList = [1, 3, 5, 7, 9]
+anonCopy = anonList
+implicitCreation = [2, 4, 6, 8, 10]
+
+anonList += 11
+println anonList // => [1, 3, 5, 7, 9, 11]
+
+two = implicitCreation[0]
+assert two == 2
+
+// To get the last index of a list, you can use size()
+// but you never would
+lastIdx = aref.size() - 1
+
+// Normally, though, you'd use an index of -1 for the last
+// element, -2 for the second last, etc.
+println implicitCreation[-1]
+//=> 10
+
+// And if you were looping through (and not using a list closure operator)
+(0..<aref.size()).each{ /* do something */ }
+
+numItems = aref.size()
+
+assert anArray instanceof int[]
+assert anArray.class.isArray()
+println anArray
+
+myList.sort() // sort is in place.
+myList += "an item" // append item
+
+def createList() { return [] }
+aref1 = createList()
+aref2 = createList()
+// aref1 and aref2 point to different lists.
+
+println anonList[4] // refers to the 4th item in the list_ref list.
+
+// The following two statements are equivalent and return up to 3 elements
+// at indices 3, 4, and 5 (if they exist).
+x = anonList[3..5]
+x = anonList[(3..5).step(1)]
+
+// This will insert 3 elements, overwriting elements at indices 3,4, or 5 - if they exist.
+anonList[3..5] = ["blackberry", "blueberry", "pumpkin"]
+
+// non index-based looping
+for (item in anonList) println item
+anonList.each{ println it }
+
+// index-based looping
+(0..<anonList.size()).each{ idx -> println anonList[idx] }
+for (idx in 0..<anonList.size()) println anonList[idx]
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.2
+//----------------------------------------------------------------------------------
+// Making Hashes of Arrays
+hash = [:] // empty map
+hash["KEYNAME"] = "new value"
+
+hash.each{ key, value -> println key + ' ' + value }
+
+hash["a key"] = [3, 4, 5]
+values = hash["a key"]
+
+hash["a key"] += 6
+println hash
+// => ["KEYNAME":"new value", "a key":[3, 4, 5, 6]]
+
+// attempting to access a value for a key not in the map yields null
+assert hash['unknown key'] == null
+assert hash.get('unknown key', 45) == 45
+println hash
+// => ["unknown key":45, "KEYNAME":"new value", "a key":[3, 4, 5, 6]]
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.3
+//----------------------------------------------------------------------------------
+// Hashes are no different to other objects
+myHash = [ key1:100, key2:200 ]
+myHashCopy = myHash.clone()
+
+value = myHash['key1']
+value = myHash.'key1'
+slice = myHash[1..3]
+keys = myHash.keySet()
+
+assert myHash instanceof Map
+
+[myHash, hash].each{ m ->
+ m.each{ k, v -> println "$k => $v"}
+}
+// =>
+// key1 => 100
+// key2 => 200
+// unknown key => 45
+// KEYNAME => new value
+// a key => [3, 4, 5, 6]
+
+values = ['key1','key2'].collect{ myHash[it] }
+println values // => [100, 200]
+
+for (key in ["key1", "key2"]) {
+ myHash[key] += 7
+}
+println myHash // => ["key1":107, "key2":207]
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.4
+//----------------------------------------------------------------------------------
+// you can use closures or the &method notation
+def joy() { println 'joy' }
+def sullen() { println 'sullen' }
+angry = { println 'angry' }
+commands = [happy: this.&joy,
+ sad: this.&sullen,
+ done: { System.exit(0) },
+ mad: angry
+]
+
+print "How are you?"
+cmd = System.in.readLine()
+if (cmd in commands.keySet()) commands[cmd]()
+else println "No such command: $cmd"
+
+
+// a counter of the type referred to in the original cookbook
+// would be implemented using a class
+def counterMaker(){
+ def start = 0
+ return { -> start++; start-1 }
+}
+
+counter = counterMaker()
+5.times{ print "${counter()} " }; println()
+
+counter1 = counterMaker()
+counter2 = counterMaker()
+
+5.times{ println "${counter1()} " }
+println "${counter1()} ${counter2()}"
+//=> 0
+//=> 1
+//=> 2
+//=> 3
+//=> 4
+//=> 5 0
+
+
+def timestamp() {
+ def start = System.currentTimeMillis()
+ return { (System.currentTimeMillis() - start).intdiv(1000) }
+}
+early = timestamp()
+//sleep(10000)
+later = timestamp()
+sleep(2000)
+println "It's been ${early()} seconds since early."
+println "It's been ${later()} seconds since later."
+//=> It's been 12 seconds since early.
+//=> It's been 2 seconds since later.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.5
+//----------------------------------------------------------------------------------
+// All variables in Groovy are objects including primitives. Some objects
+// are immutable. Some operations on objects change mutable objects.
+// Some operations produce new objects.
+
+// 15 is an Integer which is an immutable object.
+// passing 15 to a method passes a reference to the Integer object.
+def print(n) { println "${n.toString()}" }
+print(15) // no need to create any kind of explicit reference
+
+// even though Integers are immutable, references to them are not
+x = 1
+y = x
+println "$x $y" // => 1 1
+x += 1 // "x" now refers to a different object than y
+println "$x $y" // => 2 1
+y = 4 // "y" now refers to a different object than it did before
+println "$x $y" // => 2 4
+
+// Some objects (including ints and strings) are immutable, however, which
+// can give the illusion of a by-value/by-reference distinction:
+list = [[1], 1, 's']
+list.each{ it += 1 } // plus operator doesn't operate inplace
+print list //=> [[1] 1 s]
+list = list.collect{ it + 1 }
+print list //=> [[1, 1], 2, s1]
+
+list = [['Z', 'Y', 'X'], ['C', 'B', 'A'], [5, 3, 1]]
+list.each{ it.sort() } // sort operation operates inline
+println list // => [["X", "Y", "Z"], ["A", "B", "C"], [1, 3, 5]]
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.6
+//----------------------------------------------------------------------------------
+// As indicated by the previous section, everything is referenced, so
+// just create a list as normal, and beware that augmented assignment
+// works differently with immutable objects to mutable ones and depends
+// on the semantics of the particular operation invoked:
+mylist = [1, "s", [1]]
+print mylist
+//=> [1, s, [1]]
+
+mylist.each{ it *= 2 }
+print mylist
+//=> [1, s, [1,1]]
+
+mylist[0] *= 2
+mylist[-1] *= 2
+print mylist
+//=> [2, s, [1, 1]]
+
+// If you need to modify every value in a list, you use collect
+// which does NOT modify inplace but rather returns a new collection:
+mylist = 1..4
+println mylist.collect{ it**3 * 4/3 * Math.PI }
+// => [4.188790204681671, 33.510321638395844, 113.09733552923255, 268.0825731062243]
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.7
+//----------------------------------------------------------------------------------
+def mkcounter(count) {
+ def start = count
+ def bundle = [:]
+ bundle.'NEXT' = { count += 1 }
+ bundle.'PREV' = { count -= 1 }
+ bundle.'RESET' = { count = start }
+ bundle["LAST"] = bundle["PREV"]
+ return bundle
+}
+
+c1 = mkcounter(20)
+c2 = mkcounter(77)
+
+println "next c1: ${c1["NEXT"]()}" // 21
+println "next c2: ${c2["NEXT"]()}" // 78
+println "next c1: ${c1["NEXT"]()}" // 22
+println "last c1: ${c1["PREV"]()}" // 21
+println "last c1: ${c1["LAST"]()}" // 20
+println "old c2: ${c2["RESET"]()}" // 77
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.8
+//----------------------------------------------------------------------------------
+def addAndMultiply(a, b) {
+ println "${a+b} ${a*b}"
+}
+methRef = this.&addAndMultiply
+// or use direct closure
+multiplyAndAdd = { a,b -> println "${a*b} ${a+b}" }
+// later ...
+methRef(2,3) // => 5 6
+multiplyAndAdd(2,3) // => 6 5
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.9
+//----------------------------------------------------------------------------------
+record = [
+ "name": "Jason",
+ "empno": 132,
+ "title": "deputy peon",
+ "age": 23,
+ "salary": 37000,
+ "pals": ["Norbert", "Rhys", "Phineas"],
+]
+println "I am ${record.'name'}, and my pals are ${record.'pals'.join(', ')}."
+// => I am Jason, and my pals are Norbert, Rhys, Phineas.
+
+byname = [:]
+byname[record["name"]] = record
+
+rp = byname.get("Aron")
+if (rp) println "Aron is employee ${rp["empno"]}."
+
+byname["Jason"]["pals"] += "Theodore"
+println "Jason now has ${byname['Jason']['pals'].size()} pals."
+
+byname.each{ name, record ->
+ println "$name is employee number ${record['empno']}."
+}
+
+employees = [:]
+employees[record["empno"]] = record
+
+// lookup by id
+rp = employees[132]
+if (rp) println "Employee number 132 is ${rp.'name'}."
+
+byname["Jason"]["salary"] *= 1.035
+println record
+// => ["pals":["Norbert", "Rhys", "Phineas", "Theodore"], "age":23,
+// "title":"deputy peon", "name":"Jason", "salary":38295.000, "empno":132]
+
+peons = employees.findAll{ k, v -> v.'title' =~ /(?i)peon/ }
+assert peons.size() == 1
+tsevens = employees.findAll{ k, v -> v.'age' == 27 }
+assert tsevens.size() == 0
+
+// Go through all records
+println 'Names are: ' + employees.values().collect{r->r.'name'}.join(', ')
+
+byAge = {a,b-> a.value().'age' <=> b.value().'age'}
+employees.values().sort{byAge}.each{ r->
+ println "${r.'name'} is ${r.'age'}"
+}
+
+// byage, a hash: age => list of records
+byage = [:]
+byage[record["age"]] = byage.get(record["age"], []) + [record]
+
+byage.each{ age, list ->
+ println "Age $age: ${list.collect{it.'name'}.join(', ')}"
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.10
+//----------------------------------------------------------------------------------
+// if you are using a Properties (see 8.16) then just use load
+// and store (or storeToXML)
+// variation to original cookbook as Groovy can use Java's object serialization
+map = [1:'Jan', 2:'Feb', 3:'Mar']
+// write
+new File('months.dat').withObjectOutputStream{ oos ->
+ oos.writeObject(map)
+}
+// reset
+map = null
+// read
+new File('months.dat').withObjectInputStream{ ois ->
+ map = ois.readObject()
+}
+println map // => [1:"Jan", 2:"Feb", 3:"Mar"]
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.11
+//----------------------------------------------------------------------------------
+// Groovy automatically does pretty printing for some of the key types, e.g.
+mylist = [[1,2,3], [4, [5,6,7], 8,9, [0,3,5]], 7, 8]
+println mylist
+// => [[1, 2, 3], [4, [5, 6, 7], 8, 9, [0, 3, 5]], 7, 8]
+
+mydict = ["abc": "def", "ghi":[1,2,3]]
+println mydict
+// => ["abc":"def", "ghi":[1, 2, 3]]
+
+// if you have another type of object you can use the built-in dump() method
+class PetLover {
+ def name
+ def age
+ def pets
+}
+p = new PetLover(name:'Jason', age:23, pets:[dog:'Rover',cat:'Garfield'])
+println p
+// => PetLover@b957ea
+println p.dump()
+// => <PetLover@b957ea name=Jason age=23 pets=["cat":"Garfield", "dog":"Rover"]>
+
+// If that isn't good enough, you can use Boost (http://tara-indigo.org/daisy/geekscape/g2/128)
+// or Jakarta Commons Lang *ToStringBuilders (jakarta.apache.org/commons)
+// Here's an example of Boost, just extend the supplied Primordial class
+import au.net.netstorm.boost.primordial.Primordial
+class PetLover2 extends Primordial { def name, age, pets }
+println new PetLover2(name:'Jason', age:23, pets:[dog:'Rover',cat:'Garfield'])
+// =>
+// PetLover2[
+// name=Jason
+// age=23
+// pets={cat=Garfield, dog=Rover}
+// metaClass=groovy.lang.MetaClassImpl@1d8d39f[class PetLover2]
+// ]
+
+// using Commons Lang ReflectionToStringBuilder (equivalent to dump())
+import org.apache.commons.lang.builder.*
+class PetLover3 {
+ def name, age, pets
+ String toString() {
+ ReflectionToStringBuilder.toString(this)
+ }
+}
+println new PetLover3(name:'Jason', age:23, pets:[dog:'Rover',cat:'Garfield'])
+// => PetLover3@196e136[name=Jason,age=23,pets={cat=Garfield, dog=Rover}]
+
+// using Commons Lang ToStringBuilder if you want a custom format
+class PetLover4 {
+ def name, dob, pets
+ String toString() {
+ def d1 = dob.time; def d2 = (new Date()).time
+ int age = (d2 - d1)/1000/60/60/24/365 // close approx good enough here
+ return new ToStringBuilder(this).
+ append("Pet Lover's name", name).
+ append('Pets', pets).
+ append('Age', age)
+ }
+}
+println new PetLover4(name:'Jason', dob:new Date(83,03,04), pets:[dog:'Rover',cat:'Garfield'])
+// => PetLover4@fdfc58[Pet Lover's name=Jason,Pets={cat=Garfield, dog=Rover},Age=23]
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.12
+//----------------------------------------------------------------------------------
+oldlist = [1, 2, 3]
+newlist = new ArrayList(oldlist) // shallow copy
+newlist = oldlist.clone() // shallow copy
+
+oldmap = [a:1, b:2, c:3]
+newmap = new HashMap(oldmap) // shallow copy
+newmap = oldmap.clone() // shallow copy
+
+oldarray = [1, 2, 3] as int[]
+newarray = oldarray.clone()
+
+// shallow copies copy a data structure, but don't copy the items in those
+// data structures so if there are nested data structures, both copy and
+// original will refer to the same object
+mylist = ["1", "2", "3"]
+newlist = mylist.clone()
+mylist[0] = "0"
+println "$mylist $newlist"
+//=> ["0", "2", "3"] ["1", "2", "3"]
+
+mylist = [["1", "2", "3"], 4]
+newlist = mylist.clone()
+mylist[0][0] = "0"
+println "$mylist $newlist"
+//=> [["0", "2", "3"], 4] [["0", "2", "3"], 4]
+
+// standard deep copy implementation
+def deepcopy(orig) {
+ bos = new ByteArrayOutputStream()
+ oos = new ObjectOutputStream(bos)
+ oos.writeObject(orig); oos.flush()
+ bin = new ByteArrayInputStream(bos.toByteArray())
+ ois = new ObjectInputStream(bin)
+ return ois.readObject()
+}
+
+newlist = deepcopy(oldlist) // deep copy
+newmap = deepcopy(oldmap) // deep copy
+
+mylist = [["1", "2", "3"], 4]
+newlist = deepcopy(mylist)
+mylist[0][0] = "0"
+println "$mylist $newlist"
+//=> [["0", "2", "3"], 4] [["1", "2", "3"], 4]
+
+// See also:
+// http://javatechniques.com/public/java/docs/basics/low-memory-deep-copy.html
+// http://javatechniques.com/public/java/docs/basics/faster-deep-copy.html
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.13
+//----------------------------------------------------------------------------------
+// use Java's serialization capabilities as per 11.10
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.14
+//----------------------------------------------------------------------------------
+// There are numerous mechanisms for persisting objects to disk
+// using Groovy and Java mechanisms. Some are completely transparent,
+// some require some initialization only, others make the persistence
+// mechanisms visible. Here is a site that lists over 20 options:
+// http://www.java-source.net/open-source/persistence
+// (This list doesn't include EJB offerings which typically
+// require an application server or XML-based options)
+
+// We'll just consider one possibility from prevayler.sf.net.
+// This package doesn't make changes to persistent data transparent;
+// instead requiring an explicit call via a transaction object.
+// It saves all such transaction objects in a journal file so
+// that it can rollback the system any number of times (or if
+// you make use of the timestamp feature) to a particular point
+// in time. It can also be set up to create snapshots which
+// consolidate all changes made up to a certain point. The
+// journalling will begin again from that point.
+import org.prevayler.*
+class ImportantHash implements Serializable {
+ private map = [:]
+ def putAt(key, value) { map[key] = value }
+ def getAt(key) { map[key] }
+}
+class StoreTransaction implements Transaction {
+ private val
+ StoreTransaction(val) { this.val = val }
+ void executeOn(prevayler, Date ignored) { prevayler.putAt(val,val*2) }
+}
+def save(n){ store.execute(new StoreTransaction(n)) }
+store = PrevaylerFactory.createPrevayler(new ImportantHash(), "pleac11")
+hash = store.prevalentSystem()
+for (i in 0..1000) {
+ save(i)
+}
+println hash[750] // => 1500
+
+store = null; hash = null // *** could shutdown here
+
+store = PrevaylerFactory.createPrevayler(new ImportantHash(), "pleac11")
+hash = store.prevalentSystem()
+println hash[750] // => 1500
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_11.15
+//----------------------------------------------------------------------------------
+// bintree - binary tree demo program
+class BinaryTree {
+ def value, left, right
+ BinaryTree(val) {
+ value = val
+ left = null
+ right = null
+ }
+
+ // insert given value into proper point of
+ // provided tree. If no tree provided,
+ // use implicit pass by reference aspect of @_
+ // to fill one in for our caller.
+ def insert(val) {
+ if (val < value) {
+ if (left) left.insert(val)
+ else left = new BinaryTree(val)
+ } else if (val > value) {
+ if (right) right.insert(val)
+ else right = new BinaryTree(val)
+ } else println "double" // ignore double values
+ }
+
+ // recurse on left child,
+ // then show current value,
+ // then recurse on right child.
+ def inOrder() {
+ if (left) left.inOrder()
+ print value + ' '
+ if (right) right.inOrder()
+ }
+
+ // show current value,
+ // then recurse on left child,
+ // then recurse on right child.
+ def preOrder() {
+ print value + ' '
+ if (left) left.preOrder()
+ if (right) right.preOrder()
+ }
+
+ // show current value,
+ // then recurse on left child,
+ // then recurse on right child.
+ def dumpOrder() {
+ print this.dump() + ' '
+ if (left) left.dumpOrder()
+ if (right) right.dumpOrder()
+ }
+
+ // recurse on left child,
+ // then recurse on right child,
+ // then show current value.
+ def postOrder() {
+ if (left) left.postOrder()
+ if (right) right.postOrder()
+ print value + ' '
+ }
+
+ // find out whether provided value is in the tree.
+ // if so, return the node at which the value was found.
+ // cut down search time by only looking in the correct
+ // branch, based on current value.
+ def search(val) {
+ if (val == value) {
+ return this.dump()
+ } else if (val < value) {
+ return left ? left.search(val) : null
+ } else {
+ return right ? right.search(val) : null
+ }
+ }
+}
+
+// first generate 20 random inserts
+test = new BinaryTree(500)
+rand = new Random()
+20.times{
+ test.insert(rand.nextInt(1000))
+}
+
+// now dump out the tree all three ways
+print "Pre order: "; test.preOrder(); println ""
+print "In order: "; test.inOrder(); println ""
+print "Post order: "; test.postOrder(); println ""
+
+println "\nSearch?"
+while ((item = System.in.readLine()?.trim()) != null) {
+ println test.search(item.toInteger())
+ println "\nSearch?"
+}
+// Randomly produces a tree such as:
+// -------- 500 ------
+// / \
+// 181 847
+// / \ / \
+// 3 204 814 970
+// \ / \ /
+// 126 196 414 800
+// / \ /
+// 353 438 621
+// / / \
+// 423 604 776
+// / /
+// 517 765
+// /
+// 646
+// /
+// 630
+// Pre order:
+// 500 181 3 126 204 196 414 353 438 423 847 814 800 621 604 517 776 765 646 630 970
+// In order:
+// 3 126 181 196 204 353 414 423 438 500 517 604 621 630 646 765 776 800 814 847 970
+// Post order:
+// 126 3 196 353 423 438 414 204 181 517 604 630 646 765 776 621 800 814 970 847 500
+//
+// Search?
+// 125
+// null
+//
+// Search?
+// 126
+// <BinaryTree@ae97c4 value=126 left=null right=null>
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.0
+//----------------------------------------------------------------------------------
+// Groovy adopts many of the Java structuring conventions and terminology
+// and adds some concepts of its own.
+// Code-reuse can occur at the script, class, library, component or framework level.
+// Source code including class file source and scripts are organised into packages.
+// These can be thought of as like hierarchical folders or directories. Two class
+// with the same name can be distinguished by having different packages. Compiled
+// byte code and sometimes source code including scripts can be packaged up into
+// jar files. Various conventions exist for packaging classes and resources in
+// such a way to allow them to be easily reused. Some of these conventions allow
+// reusable code to be placed within repositories for easy use by third parties.
+// One such repository is the maven repository, e.g.: ibiblio.org/maven2
+// When reusing classes, it is possible to compartmentalise knowledge of
+// particular packages using multiple (potentially hierarchical) classloaders.
+// By convention, package names are all lowercase. Class names are capitalized.
+// Naming examples:
+// package my.package1.name // at most one per source file - at top of file
+// class MyClass ... // actually defines my.package1.name.MyClass
+// import my.package1.name.MyClass // allows package to be dropped within current file
+// import my.package2.name.MyClass // if class basenames are the same, can't
+// // import both, leave one fully qualified
+// import my.package.name.* // all classes in package can drop package prefix
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.1
+//----------------------------------------------------------------------------------
+// No equivalent export process exists for Groovy.
+
+// If you have some Groovy functionality that you would like others to use
+// you either make the source code available or compile it into class files
+// and package those up in a jar file. Some subset of your class files will
+// define the OO interface to your functionality, e.g. public methods,
+// interfaces, etc. Depending on the circumstances, various conventions are
+// used to indicate this functionality including Manifest files, javadocs,
+// deployment descriptors, project metadata and dependency management files.
+// See 12.18 for an example.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.2
+//----------------------------------------------------------------------------------
+// Groovy supports both static and dynamic (strong) typing. When trying to
+// compile or run files using static typing, the required classes referenced
+// must be available. Classes used in more dynamic ways may be loaded (or
+// created) at runtime. Errors in loading such dynamic cases are handled
+// using the normal exception handling mechanisms.
+
+// attempt to load an unknown resource or script:
+try {
+ evaluate(new File('doesnotexist.groovy'))
+} catch (Exception FileNotFoundException) {
+ println 'File not found, skipping ...'
+}
+// => File not found, skipping ...
+
+// attempt to load an unknown class:
+try {
+ Class.forName('org.happytimes.LottoNumberGenerator')
+} catch (ClassNotFoundException ex) {
+ println 'Class not found, skipping ...'
+}
+// -> Class not found, skipping ...
+
+// dynamicallly look for a database driver (slight variation to original cookbook)
+// Note: this hypothetical example ignores certain issues e.g. different url
+// formats for configuration when establishing a connection with the driver
+candidates = [
+ 'oracle.jdbc.OracleDriver',
+ 'com.ibm.db2.jcc.DB2Driver',
+ 'com.microsoft.jdbc.sqlserver.SQLServerDriver',
+ 'net.sourceforge.jtds.jdbc.Driver',
+ 'com.sybase.jdbc3.jdbc.SybDriver',
+ 'com.informix.jdbc.IfxDriver',
+ 'com.mysql.jdbc.Driver',
+ 'org.postgresql.Driver',
+ 'com.sap.dbtech.jdbc.DriverSapDB',
+ 'org.hsqldb.jdbcDriver',
+ 'com.pointbase.jdbc.jdbcUniversalDriver',
+ 'org.apache.derby.jdbc.ClientDriver',
+ 'com.mckoi.JDBCDriver',
+ 'org.firebirdsql.jdbc.FBDriver',
+ 'sun.jdbc.odbc.JdbcOdbcDriver'
+]
+loaded = null
+for (driver in candidates) {
+ try {
+ loaded = Class.forName(driver).newInstance()
+ break
+ } catch (Exception ex) { /* ignore */ }
+}
+println loaded?.class?.name // => sun.jdbc.odbc.JdbcOdbcDriver
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.3
+//----------------------------------------------------------------------------------
+// In Groovy (like Java), any static reference to an external class within
+// your class will cause the external class to be loaded from the classpath.
+// You can dynamically add to the classpath using:
+// this.class.rootLoader.addURL(url)
+// To delay loading of external classes, use Class.forName() or evaluate()
+// the script separately as shown in 12.2.
+
+// For the specific case of initialization code, here is another example:
+// (The code within the anonymous { ... } block is called whenever the
+// class is loaded.)
+class DbHelper {
+ def driver
+ {
+ if (System.properties.'driver' == 'oracle')
+ driver = Class.forName('oracle.jdbc.OracleDriver')
+ else
+ driver = Class.forName('sun.jdbc.odbc.JdbcOdbcDriver')
+ }
+}
+println new DbHelper().driver.name // => sun.jdbc.odbc.JdbcOdbcDriver
+// call program with -Ddriver=oracle to swap to other driver
+
+// A slightly related feature: If you want to load a script (typically in a
+// server environment) whenever the source file changes, use GroovyScriptEngine()
+// instead of GroovyShell() when embedding groovy.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.4
+//----------------------------------------------------------------------------------
+// class variables are private unless access functions are defined
+class Alpha {
+ def x = 10
+ private y = 12
+}
+
+println new Alpha().x // => 10
+println new Alpha().y // => 12 when referenced inside source file, error outside
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.5
+//----------------------------------------------------------------------------------
+// You can examine the stacktrace to determine the calling class: see 10.4
+// When executing a script from a groovy source file, you can either:
+println getClass().classLoader.resourceLoader.loadGroovySource(getClass().name)
+// => file:/C:/Projects/GroovyExamples/Pleac/classes/pleac12.groovy
+// or for the initially started script when started using the standard .bat/.sh files
+println System.properties.'script.name'
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.6
+//----------------------------------------------------------------------------------
+// For code which executes at class startup, see the initialization code block
+// mechanism mentioned in 12.3. For code which should execute during shutdown
+// see the finalize() method discussed (including limitations) in 13.2.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.7
+//----------------------------------------------------------------------------------
+// Each JVM process may have its own classpath (and indeed its own version of Java
+// runtime and libraries). You "simply" supply a classpath pointing to different
+// locations to obtain different modules.
+// Groovy augments the JVM behaviour by allowing individuals to have a ~/.groovy/lib
+// directory with additional libraries (and potentially other resources).
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.8
+//----------------------------------------------------------------------------------
+// To make your code available to others could involve any of the following:
+// (1) make your source code available
+// (2) if you are creating a standard class, use the jar tool to package the
+// compiled code into a jar - this is then added to the classpath to use
+// (3) if the jar relies on additional jars, this is sometimes specified in
+// a special manifest file within the jar
+// (4) if the code is designed to run within a container environment, there
+// might be additional packaging, e.g. servlets might be packaged in a war
+// file - essentially a jar file with extra metadata in xml format.
+// (5) you might also supply your package to a well known repository such as the
+// maven repository - and you will add dependency information in xml format
+// (6) you may use platform specific installers to produce easily installable
+// components (e.g. windows .exe files or linux rpm's)
+// (7) you may spackage up your components as a plugin (e.g. as an eclipse plugin)
+// this is also typically in jar/zip like format with additional metadata
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.9
+//----------------------------------------------------------------------------------
+// Groovy has no SelfLoader. Class loading can be delayed using external scripts
+// and by using the Class.forName() approach discussed in 12.2/12.3. If you have
+// critical performance issues, you can use these techniques and keep your class
+// size small to maximise the ability to defer loading. There are other kinds of
+// performance tradeoffs you can make too. Alot of work has been done with JIT
+// (just in time) compilers for bytecode. You can pre-compile Groovy source files
+// into bytecode using the groovy compiler (groovyc). You can also do this on
+// the fly for scripts you know you are going to need shortly.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.10
+//----------------------------------------------------------------------------------
+// Groovy has no AutoLoader. See the discussion in 12.9 for some techniques to
+// impact program performance. There are many techniques available to speed up
+// program performance (and in particular load speed). Some of these utilise
+// techniques similar in nature to the technique used by the AutoLoader.
+// As already mentioned, when you load a class into the JVM, any statically
+// referenced class is also loaded. If you reference interfaces rather than
+// concrete implementations, only the interface need be loaded. If you must
+// reference a concrete implementation you can use either a Proxy class or
+// classloader tricks to delay the loading of a full class (e.g. you supply a
+// Proxy class with just one method implemented or a lazy-loading Proxy which
+// loads the real class only when absolutely required)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.11
+//----------------------------------------------------------------------------------
+// You can use Categories to override Groovy and Java base functionality.
+println new Date().time // => 1169019557140
+
+class DateCategory { // the class name by convention ends with category
+ // we can add new functionality
+ static float getFloatTime(Date self) {
+ return (float) self.getTime()
+ }
+ // we can override existing functionality (now seconds since 1970 not millis)
+ static long asSeconds(Date self) {
+ return (long) (self.getTime()/1000)
+ }
+}
+
+use (DateCategory) {
+ println new Date().floatTime // => 1.1690195E12
+ println new Date().asSeconds() // => 1169019557
+}
+
+// We can also use the 'as' keyword
+class MathLib {
+ def triple(n) { n * 4 }
+ def twice(n) { n * 2 }
+}
+def m = new MathLib()
+println m.twice(10) // => 20
+println m.triple(10) // => 40 (Intentional Bug!)
+// we might want to make use of some funtionality in the math
+// library but want to later some of its features slightly or fix
+// some bugs, we can simply import the original using a different name
+import MathLib as BuggyMathLib
+// now we could define our own MathLib which extended or had a delegate
+// of the BuggyMathLib class
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.12
+//----------------------------------------------------------------------------------
+// Many Java and Groovy programs emit a stacktrace when an error occurs.
+// This shows both the calling and called programs (with line numbers if
+// supplied). Groovy pretties up stacktraces to show less noise. You can use -d
+// or --debug on the commandline to force it to always produce full stacktraces.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.13
+//----------------------------------------------------------------------------------
+// already have log10, how to create log11 to log100
+(11..100).each { int base ->
+ binding."log$base" = { int n -> Math.log(n) / Math.log(base) }
+}
+println log20(400) // => 2.0
+println log100(1000000) // => 3.0 (displays 2.9999999999999996 using doubles)
+
+// same thing again use currying
+def logAnyBase = { base, n -> Math.log(n) / Math.log(base) }
+(11..100).each { int base ->
+ binding."log$base" = logAnyBase.curry(base)
+}
+println log20(400) // => 2.0
+println log100(1000000) // => 3.0 (displays 2.9999999999999996 using doubles)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.14
+//----------------------------------------------------------------------------------
+// Groovy intefaces with C in the same way as Java: using JNI
+// For this discussion we will ignoring platform specific options and CORBA.
+// This tutorial here describes how to allow Java (and hence Groovy) to
+// call a C program which generates UUIDs:
+// http://ringlord.com/publications/jni-howto/
+// Here's another useful reference:
+// http://weblogs.java.net/blog/kellyohair/archive/2006/01/compilation_of_1.html
+// And of course, Sun's tutorial:
+// http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jni.html
+
+// You might also want to consider SWIG which simplifies connecting
+// C/C++ to many scripting languages including Java (and hence Groovy)
+// More details: http://www.swig.org/
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.15
+//----------------------------------------------------------------------------------
+// See discussion for 12.14
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.16
+//----------------------------------------------------------------------------------
+// The standard documentation system for Java is JavaDoc.
+// Documentation for JavaDoc is part of a Java installation.
+// Groovy has a GroovyDoc tool planned which expands upon the JavaDoc tool
+// but work on the tool hasn't progressed much as yet.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.17
+//----------------------------------------------------------------------------------
+// Most libraries for Java (and hence Groovy) come precompiled. You simply download
+// the jar and place it somewhere on your CLASSPATH.
+
+// If only source code is available, you need to download the source and follow any
+// instuctions which came with the source. Most projects use one of a handful of
+// build tools to compile, test and package their artifacts. Typical ones are Ant
+// and Maven which you need to install according to their respective instructions.
+
+// If using Ant, you need to unpack the source files then type 'ant'.
+
+// If using Maven, you need to unpack the source files then type 'maven'.
+
+// If you are using Maven or Ivy for dependency management you can add
+// the following lines to your project description file.
+/*
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <version>3.2</version>
+ </dependency>
+*/
+// This will automatically download the particular version of the referenced
+// library file and also provide hooks so that you can make this automatically
+// available in your classpath.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.18
+//----------------------------------------------------------------------------------
+// example groovy file for a "module"
+import org.apache.commons.lang.WordUtils
+
+class Greeter {
+ def name
+ Greeter(who) { name = WordUtils.capitalize(who) }
+ def salute() { "Hello $name!" }
+}
+
+// test class
+class GreeterTest extends GroovyTestCase {
+ def testGreeting() {
+ assert new Greeter('world').salute()
+ }
+}
+
+// Typical Ant build file (could be in Groovy instead of XML):
+/*
+<?xml version="1.0"?>
+<project name="sample" default="jar" basedir=".">
+ <property name="src" value="src"/>
+ <property name="build" value="build"/>
+
+ <target name="init">
+ <mkdir dir="${build}"/>
+ </target>
+
+ <target name="compile" depends="init">
+ <mkdir dir="${build}/classes"/>
+ <groovyc srcdir="${src}" destdir="${build}/classes"/>
+ </target>
+
+ <target name="test" depends="compile">
+ <groovy src="${src}/GreeterTest.groovy">
+ </target>
+
+ <target name="jar" depends="compile,test">
+ <mkdir dir="${build}/jar"/>
+ <jar destfile="${build}/jar/Greeter.jar" basedir="${build}/classes">
+ <manifest>
+ <attribute name="Main-Class" value="Greeter"/>
+ </manifest>
+ </jar>
+ </target>
+</project>
+
+*/
+
+// Typical dependency management file
+/*
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>groovy</groupId>
+ <artifactId>module</artifactId>
+ <name>Greeter</name>
+ <version>1.0</version>
+ <packaging>jar</packaging>
+ <description>Greeter Module/description>
+ <dependencies>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.2</version>
+ </dependency>
+ </dependencies>
+</project>
+*/
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_12.19
+//----------------------------------------------------------------------------------
+// Searching available modules in repositories:
+// You can browse the repositories online, e.g. ibiblio.org/maven2 or various
+// plugins are available for IDEs which do this for you, e.g. JarJuggler for IntelliJ.
+
+// Searching currently "installed" modules:
+// Browse your install directory, view your maven POM file, look in your ~/.groovy/lib
+// directory, turn on debug modes and watch classloader messages ...
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.0
+//----------------------------------------------------------------------------------
+// Classes and objects in Groovy are rather straigthforward
+class Person {
+ // Class variables (also called static attributes) are prefixed by the keyword static
+ static personCounter=0
+ def age, name // this creates setter and getter methods
+ private alive
+
+ // object constructor
+ Person(age, name, alive = true) { // Default arg like in C++
+ this.age = age
+ this.name = name
+ this.alive = alive
+ personCounter += 1
+ // There is a '++' operator in Groovy but using += is often clearer.
+ }
+
+ def die() {
+ alive = false
+ println "$name has died at the age of $age."
+ alive
+ }
+
+ def kill(anotherPerson) {
+ println "$name is killing $anotherPerson.name."
+ anotherPerson.die()
+ }
+
+ // methods used as queries generally start with is, are, will or can
+ // usually have the '?' suffix
+ def isStillAlive() {
+ alive
+ }
+
+ def getYearOfBirth() {
+ new Date().year - age
+ }
+
+ // Class method (also called static method)
+ static getNumberOfPeople() { // accessors often start with get
+ // in which case you can call it like
+ // it was a field (without the get)
+ personCounter
+ }
+}
+
+// Using the class:
+// Create objects of class Person
+lecter = new Person(47, 'Hannibal')
+starling = new Person(29, 'Clarice', true)
+pazzi = new Person(40, 'Rinaldo', true)
+
+// Calling a class method
+println "There are $Person.numberOfPeople Person objects."
+
+println "$pazzi.name is ${pazzi.alive ? 'alive' : 'dead'}."
+lecter.kill(pazzi)
+println "$pazzi.name is ${pazzi.isStillAlive() ? 'alive' : 'dead'}."
+
+println "$starling.name was born in $starling.yearOfBirth."
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.1
+//----------------------------------------------------------------------------------
+// Classes may have no constructor.
+class MyClass { }
+
+aValidButNotVeryUsefulObject = new MyClass()
+
+// If no explicit constructor is given a default implicit
+// one which supports named parameters is provided.
+class MyClass2 {
+ def start = new Date()
+ def age = 0
+}
+println new MyClass2(age:4).age // => 4
+
+// One or more explicit constructors may also be provided
+class MyClass3 {
+ def start
+ def age
+ MyClass3(date, age) {
+ start = date
+ this.age = age
+ }
+}
+println new MyClass3(new Date(), 20).age // => 20
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.2
+//----------------------------------------------------------------------------------
+// Objects are destroyed by the JVM garbage collector.
+// The time of destroying is not predicated but left up to the JVM.
+// There is no direct support for destructor. There is a courtesy
+// method called finalize() which the JVM may call when disposing
+// an object. If you need to free resources for an object, like
+// closing a socket or killing a spawned subprocess, you should do
+// it explicitly - perhaps by supporting your own lifecycle methods
+// on your class, e.g. close().
+
+class MyClass4{
+ void finalize() {
+ println "Object [internal id=${hashCode()}] is dying at ${new Date()}"
+ }
+}
+
+// test code
+50.times {
+ new MyClass4()
+}
+20.times {
+ System.gc()
+}
+// => (between 0 and 50 lines similar to below)
+// Object [internal id=10884088] is dying at Wed Jan 10 16:33:33 EST 2007
+// Object [internal id=6131844] is dying at Wed Jan 10 16:33:33 EST 2007
+// Object [internal id=12245160] is dying at Wed Jan 10 16:33:33 EST 2007
+// ...
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.3
+//----------------------------------------------------------------------------------
+// You can write getter and setter methods explicitly as shown below.
+// One convention is to use set and get at the start of method names.
+class Person2 {
+ private name
+ def getName() { name }
+ def setName(name) { this.name = name }
+}
+
+// You can also just use def which auto defines default getters and setters.
+class Person3 {
+ def age, name
+}
+
+// Any variables marked as final will only have a default getter.
+// You can also write an explicit getter. For a write-only variable
+// just write only a setter.
+class Person4 {
+ final age // getter only
+ def name // getter and setter
+ private color // private
+ def setColor() { this.color = color } // setter only
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.4
+//----------------------------------------------------------------------------------
+class Person5 {
+ // Class variables (also called static attributes) are prefixed by the keyword static
+ static personCounter = 0
+
+ static getPopulation() {
+ personCounter
+ }
+ Person5() {
+ personCounter += 1
+ }
+ void finalize() {
+ personCounter -= 1
+ }
+}
+people = []
+10.times {
+ people += new Person5()
+}
+println "There are ${Person5.population} people alive"
+// => There are 10 people alive
+
+alpha = new FixedArray()
+println "Bound on alpha is $alpha.maxBounds"
+
+beta = new FixedArray()
+beta.maxBounds = 50
+println "Bound on alpha is $alpha.maxBounds"
+
+class FixedArray {
+ static maxBounds = 100
+
+ def getMaxBounds() {
+ maxBounds
+ }
+ def setMaxBounds(value) {
+ maxBounds = value
+ }
+}
+// =>
+// Bound on alpha is 100
+// Bound on alpha is 50
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.5
+//----------------------------------------------------------------------------------
+// The fields of this struct-like class are dynamically typed
+class DynamicPerson { def name, age, peers }
+p = new DynamicPerson()
+p.name = "Jason Smythe"
+p.age = 13
+p.peers = ["Wilbur", "Ralph", "Fred"]
+p.setPeers(["Wilbur", "Ralph", "Fred"]) // alternative using implicit setter
+p["peers"] = ["Wilbur", "Ralph", "Fred"] // alternative access using name of field
+println "At age $p.age, $p.name's first friend is ${p.peers[0]}"
+// => At age 13, Jason Smythe's first friend is Wilbur
+
+// The fields of this struct-like class are statically typed
+class StaticPerson { String name; int age; List peers }
+p = new StaticPerson(name:'Jason', age:14, peers:['Fred','Wilbur','Ralph'])
+println "At age $p.age, $p.name's first friend is ${p.peers[0]}"
+// => At age 14, Jason's first friend is Fred
+
+
+class Family { def head, address, members }
+folks = new Family(head:new DynamicPerson(name:'John',age:34))
+
+// supply of own accessor method for the struct for error checking
+class ValidatingPerson {
+ private age
+ def printAge() { println 'Age=' + age }
+ def setAge(value) {
+ if (!(value instanceof Integer))
+ throw new IllegalArgumentException("Argument '${value}' isn't an Integer")
+ if (value > 150)
+ throw new IllegalArgumentException("Age ${value} is unreasonable")
+ age = value
+ }
+}
+
+// test ValidatingPerson
+def tryCreate(arg) {
+ try {
+ new ValidatingPerson(age:arg).printAge()
+ } catch (Exception ex) {
+ println ex.message
+ }
+}
+
+tryCreate(20)
+tryCreate('Youngish')
+tryCreate(200)
+// =>
+// Age=20
+// Argument 'Youngish' isn't an Integer
+// Age 200 is unreasonable
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.6
+//----------------------------------------------------------------------------------
+// Groovy objects are (loosely speaking) extended Java objects.
+// Java's Object class provides a clone() method. The conventions of
+// clone() are that if I say a = b.clone() then a and b should be
+// different objects with the same type and value. Java doesn't
+// enforce a class to implement a clone() method at all let alone
+// require that one has to meet these conventions. Classes which
+// do support clone() should implement the Cloneable interface and
+// implement an equals() method.
+// Groovy follows Java's conventions for clone().
+
+class A implements Cloneable {
+ def name
+ boolean equals(Object other) {
+ other instanceof A && this.name == other.name
+ }
+}
+ob1 = new A(name:'My named thing')
+
+ob2 = ob1.clone()
+assert !ob1.is(ob2)
+assert ob1.class == ob2.class
+assert ob2.name == ob1.name
+assert ob1 == ob2
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.7
+//----------------------------------------------------------------------------------
+class CanFlicker {
+ def flicker(arg) { return arg * 2 }
+}
+methname = 'flicker'
+assert new CanFlicker().invokeMethod(methname, 10) == 20
+assert new CanFlicker()."$methname"(10) == 20
+
+class NumberEcho {
+ def one() { 1 }
+ def two() { 2 }
+ def three() { 3 }
+}
+obj = new NumberEcho()
+// call methods on the object, by name
+assert ['one', 'two', 'three', 'two', 'one'].collect{ obj."$it"() }.join() == '12321'
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.8
+//----------------------------------------------------------------------------------
+// Groovy can work with Groovy objects which inherit from a common base
+// class called GroovyObject or Java objects which inherit from Object.
+
+// the class of the object
+assert 'a string'.class == java.lang.String
+
+// Groovy classes are actually objects of class Class and they
+// respond to methods defined in the Class class as well
+assert 'a string'.class.class == java.lang.Class
+assert !'a string'.class.isArray()
+
+// ask an object whether it is an instance of particular class
+n = 4.7f
+println (n instanceof Integer) // false
+println (n instanceof Float) // true
+println (n instanceof Double) // false
+println (n instanceof String) // false
+println (n instanceof StaticPerson) // false
+
+// ask if a class or interface is either the same as, or is a
+// superclass or superinterface of another class
+println n.class.isAssignableFrom(Float.class) // true
+println n.class.isAssignableFrom(String.class) // false
+
+// can a Groovy object respond to a particular method?
+assert new CanFlicker().metaClass.methods*.name.contains('flicker')
+
+class POGO{}
+println (obj.metaClass.methods*.name - new POGO().metaClass.methods*.name)
+// => ["one", "two", "three"]
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.9
+//----------------------------------------------------------------------------------
+// Most classes in Groovy are inheritable
+class Person6{ def age, name }
+dude = new Person6(name:'Jason', age:23)
+println "$dude.name is age $dude.age."
+
+// Inheriting from Person
+class Employee extends Person6 {
+ def salary
+}
+empl = new Employee(name:'Jason', age:23, salary:200)
+println "$empl.name is age $empl.age and has salary $empl.salary."
+
+// Many built-in class can be inherited the same way
+class WierdList extends ArrayList {
+ def size() { // size method in this class is overridden
+ super.size() * 2
+ }
+}
+a = new WierdList()
+a.add('dog')
+a.add('cat')
+println a.size() // => 4
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.10
+//----------------------------------------------------------------------------------
+class Person7 { def firstname, surname; def getName(){ firstname + ' ' + surname } }
+class Employee2 extends Person7 {
+ def employeeId
+ def getName(){ 'Employee Number ' + employeeId }
+ def getRealName(){ super.getName() }
+}
+p = new Person7(firstname:'Jason', surname:'Smythe')
+println p.name
+// =>
+// Jason Smythe
+e = new Employee2(firstname:'Jason', surname:'Smythe', employeeId:12349876)
+println e.name
+println e.realName
+// =>
+// Employee Number 12349876
+// Jason Smythe
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.11
+//----------------------------------------------------------------------------------
+// Groovy's built in constructor and auto getter/setter features
+ // give you the required functionalty already but you could also
+ // override invokeMethod() for trickier scenarios.
+class Person8 {
+ def name, age, peers, parent
+ def newChild(args) { new Person8(parent:this, *:args) }
+}
+
+dad = new Person8(name:'Jason', age:23)
+kid = dad.newChild(name:'Rachel', age:2)
+println "Kid's parent is ${kid.parent.name}"
+// => Kid's parent is Jason
+
+// additional fields ...
+class Employee3 extends Person8 { def salary, boss }
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.12
+//----------------------------------------------------------------------------------
+// Fields marked as private in Groovy can't be trampled by another class in
+// the class hierarchy
+class Parent {
+ private name // my child's name
+ def setChildName(value) { name = value }
+ def getChildName() { name }
+}
+class GrandParent extends Parent {
+ private name // my grandchild's name
+ def setgrandChildName(value) { name = value }
+ def getGrandChildName() { name }
+}
+g = new GrandParent()
+g.childName = 'Jason'
+g.grandChildName = 'Rachel'
+println g.childName // => Jason
+println g.grandChildName // => Rachel
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.13
+//----------------------------------------------------------------------------------
+// The JVM garbage collector copes with circular structures.
+// You can test it with this code:
+class Person9 {
+ def friend
+ void finalize() {
+ println "Object [internal id=${hashCode()}] is dying at ${new Date()}"
+ }
+}
+
+def makeSomeFriends() {
+ def first = new Person9()
+ def second = new Person9(friend:first)
+ def third = new Person9(friend:second)
+ def fourth = new Person9(friend:third)
+ def fifth = new Person9(friend:fourth)
+ first.friend = fifth
+}
+
+makeSomeFriends()
+100.times{
+ System.gc()
+}
+// =>
+// Object [internal id=24478976] is dying at Tue Jan 09 22:24:31 EST 2007
+// Object [internal id=32853087] is dying at Tue Jan 09 22:24:31 EST 2007
+// Object [internal id=23664622] is dying at Tue Jan 09 22:24:31 EST 2007
+// Object [internal id=10630672] is dying at Tue Jan 09 22:24:31 EST 2007
+// Object [internal id=25921812] is dying at Tue Jan 09 22:24:31 EST 2007
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.14
+//----------------------------------------------------------------------------------
+// Groovy provides numerous methods which are automatically associated with
+// symbol operators, e.g. here is '<=>' which is associated with compareTo()
+// Suppose we have a class with a compareTo operator, such as:
+class Person10 implements Comparable {
+ def firstname, initial, surname
+ Person10(f,i,s) { firstname = f; initial = i; surname = s }
+ int compareTo(other) { firstname <=> other.firstname }
+}
+a = new Person10('James', 'T', 'Kirk')
+b = new Person10('Samuel', 'L', 'Jackson')
+println a <=> b
+// => -1
+
+// we can override the existing Person10's <=> operator as below
+// so that now comparisons are made using the middle initial
+// instead of the fisrtname:
+class Person11 extends Person10 {
+ Person11(f,i,s) { super(f,i,s) }
+ int compareTo(other) { initial <=> other.initial }
+}
+
+a = new Person11('James', 'T', 'Kirk')
+b = new Person11('Samuel', 'L', 'Jackson')
+println a <=> b
+// => 1
+
+// we could also in general use Groovy's categories to extend class functionality.
+
+// There is no way to directly overload the '""' (stringify)
+// operator in Groovy. However, by convention, classes which
+// can reasonably be converted to a String will define a
+// 'toString()' method as in the TimeNumber class defined below.
+// The 'println' method will automatcally call an object's
+// 'toString()' method as is demonstrated below. Furthermore,
+// an object of that class can be used most any place where the
+// interpreter is looking for a String value.
+
+//---------------------------------------
+// NOTE: Groovy has various built-in Time/Date/Calendar classes
+// which would usually be used to manipulate time objects, the
+// following is supplied for educational purposes to demonstrate
+// operator overloading.
+class TimeNumber {
+ def h, m, s
+ TimeNumber(hour, min, sec) { h = hour; m = min; s = sec }
+
+ def toDigits(s) { s.toString().padLeft(2, '0') }
+ String toString() {
+ return toDigits(h) + ':' + toDigits(m) + ':' + toDigits(s)
+ }
+
+ def plus(other) {
+ s = s + other.s
+ m = m + other.m
+ h = h + other.h
+ if (s >= 60) {
+ s %= 60
+ m += 1
+ }
+ if (m >= 60) {
+ m %= 60
+ h += 1
+ }
+ return new TimeNumber(h, m, s)
+ }
+
+}
+
+t1 = new TimeNumber(0, 58, 59)
+sec = new TimeNumber(0, 0, 1)
+min = new TimeNumber(0, 1, 0)
+println t1 + sec + min + min
+
+//-----------------------------
+// StrNum class example: Groovy's builtin String class already has the
+// capabilities outlined in StrNum Perl example, however the '*' operator
+// on Groovy's String class acts differently: It creates a string which
+// is the original string repeated N times.
+//
+// Using Groovy's String class as is in this example:
+x = "Red"; y = "Black"
+z = x+y
+r = z*3 // r is "RedBlackRedBlackRedBlack"
+println "values are $x, $y, $z, and $r"
+println "$x is ${x < y ? 'LT' : 'GE'} $y"
+// prints:
+// values are Red, Black, RedBlack, and RedBlackRedBlackRedBlack
+// Red is GE Black
+
+//-----------------------------
+class FixNum {
+ def REGEX = /(\.\d*)/
+ static final DEFAULT_PLACES = 0
+ def float value
+ def int places
+ FixNum(value) {
+ initValue(value)
+ def m = value.toString() =~ REGEX
+ if (m) places = m[0][1].size() - 1
+ else places = DEFAULT_PLACES
+ }
+ FixNum(value, places) {
+ initValue(value)
+ this.places = places
+ }
+ private initValue(value) {
+ this.value = value
+ }
+
+ def plus(other) {
+ new FixNum(value + other.value, [places, other.places].max())
+ }
+
+ def multiply(other) {
+ new FixNum(value * other.value, [places, other.places].max())
+ }
+
+ def div(other) {
+ println "DEUG: Divide = ${value/other.value}"
+ def result = new FixNum(value/other.value)
+ result.places = [places,other.places].max()
+ result
+ }
+
+ String toString() {
+ //m = value.toString() =~ /(\d)/ + REGEX
+ String.format("STR%s: %.${places}f", [this.class.name, value as float] as Object[])
+ }
+}
+
+x = new FixNum(40)
+y = new FixNum(12, 0)
+
+println "sum of $x and $y is ${x+y}"
+println "product of $x and $y is ${x*y}"
+
+z = x/y
+println "$z has $z.places places"
+z.places = 2
+println "$z now has $z.places places"
+
+println "div of $x by $y is $z"
+println "square of that is ${z*z}"
+// =>
+// sum of STRFixNum: 40 and STRFixNum: 12 is STRFixNum: 52
+// product of STRFixNum: 40 and STRFixNum: 12 is STRFixNum: 480
+// DEUG: Divide = 3.3333333333333335
+// STRFixNum: 3 has 0 places
+// STRFixNum: 3.33 now has 2 places
+// div of STRFixNum: 40 by STRFixNum: 12 is STRFixNum: 3.33
+// square of that is STRFixNum: 11.11
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_13.15
+//----------------------------------------------------------------------------------
+// Groovy doesn't use the tie terminology but you can achieve
+// similar results with Groovy's metaprogramming facilities
+class ValueRing {
+ private values
+ def add(value) { values.add(0, value) }
+ def next() {
+ def head = values[0]
+ values = values[1..-1] + head
+ return head
+ }
+}
+ring = new ValueRing(values:['red', 'blue'])
+def getColor() { ring.next() }
+void setProperty(String n, v) {
+ if (n == 'color') { ring.add(v); return }
+ super.setProperty(n,v)
+}
+
+println "$color $color $color $color $color $color"
+// => red blue red blue red blue
+
+color = 'green'
+println "$color $color $color $color $color $color"
+// => green red blue green red blue
+
+// Groovy doesn't have the $_ implicit variable so we can't show an
+// example that gets rid of it. We can however show an example of how
+// you could add in a simplified version of that facility into Groovy.
+// We use Groovy's metaProgramming facilities. We execute our script
+// in a new GroovyShell so that we don't affect subsequent examples.
+// script:
+x = 3
+println "$_"
+y = 'cat' * x
+println "$_"
+
+// metaUnderscore:
+void setProperty(String n, v) {
+ super.setProperty('_',v)
+ super.setProperty(n,v)
+}
+
+new GroovyShell().evaluate(metaUnderscore + script)
+// =>
+// 3
+// catcatcat
+
+// We can get a little bit fancier by making an UnderscoreAware class
+// that wraps up some of this functionality. This is not recommended
+// as good Groovy style but mimicks the $_ behaviour in a sinple way.
+class UnderscoreAware implements GroovyInterceptable {
+ private _saved
+ void setProperty(String n, v) {
+ _saved = v
+ this.metaClass.setProperty(this, n, v)
+ }
+ def getProperty(String n) {
+ if (n == '_') return _saved
+ this.metaClass.getProperty(this, n)
+ }
+ def invokeMethod(String name, Object args) {
+ if (name.startsWith('print') && args.size() == 0)
+ args = [_saved] as Object[]
+ this.metaClass.invokeMethod(this, name, args)
+ }
+}
+
+class PerlishClass extends UnderscoreAware {
+ private _age
+ def setAge(age){ _age = age }
+ def getAge(){ _age }
+ def test() {
+ age = 25
+ println "$_" // explicit $_ supported
+ age++
+ println() // implicit $_ will be injected
+ }
+}
+
+def x = new PerlishClass()
+x.test()
+// =>
+// 25
+// 26
+
+// Autoappending hash:
+class AutoMap extends HashMap {
+ void setProperty(String name, v) {
+ if (containsKey(name)) {
+ put(name, get(name) + v)
+ } else {
+ put(name, [v])
+ }
+ }
+}
+m = new AutoMap()
+m.beer = 'guinness'
+m.food = 'potatoes'
+m.food = 'peas'
+println m
+// => ["food":["potatoes", "peas"], "beer":["guinness"]]
+
+// Case-Insensitive Hash:
+class FoldedMap extends HashMap {
+ void setProperty(String name, v) {
+ put(name.toLowerCase(), v)
+ }
+ def getProperty(String name) {
+ get(name.toLowerCase())
+ }
+}
+tab = new FoldedMap()
+tab.VILLAIN = 'big '
+tab.herOine = 'red riding hood'
+tab.villain += 'bad wolf'
+println tab
+// => ["heroine":"red riding hood", "villain":"big bad wolf"]
+
+// Hash That "Allows Look-Ups by Key or Value":
+class RevMap extends HashMap {
+ void setProperty(String n, v) { put(n,v); put(v,n) }
+ void remove(n) { super.remove(get(n)); super.remove(n) }
+}
+rev = new RevMap()
+rev.Rojo = 'Red'
+rev.Azul = 'Blue'
+rev.Verde = 'Green'
+rev.EVIL = [ "No way!", "Way!!" ]
+rev.remove('Red')
+rev.remove('Azul')
+println rev
+// =>
+// [["No way!", "Way!!"]:"EVIL", "EVIL":["No way!", "Way!!"], "Verde":"Green", "Green":"Verde"]
+
+// Infinite loop scenario:
+// def x(n) { x(++n) }; x(0)
+// => Caught: java.lang.StackOverflowError
+
+// Multiple Strrams scenario:
+class MultiStream extends PrintStream {
+ def streams
+ MultiStream(List streams) {
+ super(streams[0])
+ this.streams = streams
+ }
+ def println(String x) {
+ streams.each{ it.println(x) }
+ }
+}
+tee = new MultiStream([System.out, System.err])
+tee.println ('This goes two places')
+// =>
+// This goes two places
+// This goes two places
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_14.0
+//----------------------------------------------------------------------------------
+As discussed in 14.1, many database options exist, one of which is JDBC.
+Over 200 JDBC drivers are listed at the following URL:
+http://developers.sun.com/product/jdbc/drivers/browse_all.jsp
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_14.1
+//----------------------------------------------------------------------------------
+// Groovy can make use of various Java persistence libraries and has special
+// support built-in (e.g. datasets) for interacting wth RDBMS systems.
+// Some of the options include:
+// object serialization (built in to Java)
+// pbeans: pbeans.sf.net
+// prevayler: http://www.prevayler.org
+// Berkeley DB Java edition: http://www.oracle.com/database/berkeley-db/je/
+// JDBC: Over 200 drivers are listed at http://developers.sun.com/product/jdbc/drivers
+// Datasets (special Groovy support)
+// XML via e.g. xstream or JAXB or XmlBeans or ...
+// ORM: over 20 are listed at http://java-source.net/open-source/persistence
+// JNI: can be used directly on a platform that supports e.g. DBM or via
+// a cross platform API such as Apache APR which includes DBM routines:
+// http://apr.apache.org/docs/apr-util/0.9/group__APR__Util__DBM.html
+// jmork: used for Firefox/Thunderbird databases, e.g. address books, history files
+// JDBC or Datasets would normally be most common for all examples in this chapter.
+
+
+// Example shown using berkeley db Java edition - not quite as transparent as
+// cookbook example as Berkeley DB Java addition makes transactions visible.
+import com.sleepycat.je.*
+tx = null
+envHome = new File("D:/Projects/GroovyExamples/Pleac/data/db")
+
+myEnvConfig = new EnvironmentConfig()
+myEnvConfig.setAllowCreate(true)
+myEnv = new Environment(envHome, myEnvConfig)
+
+myDbConfig = new DatabaseConfig()
+myDbConfig.setAllowCreate(true)
+myDb = myEnv.openDatabase(tx, "vendorDB", myDbConfig)
+
+theKey = new DatabaseEntry("key".getBytes("UTF-8"))
+theData = new DatabaseEntry("data".getBytes("UTF-8"))
+myDb.put(tx, theKey, theData)
+if (myDb.get(tx, theKey, theData, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
+ key = new String(theKey.data, "UTF-8")
+ foundData = new String(theData.data, "UTF-8")
+ println "For key: '$key' found data: '$foundData'."
+}
+myDb.delete(tx, theKey)
+myDb.close()
+myEnv.close()
+
+
+// userstats using pbeans
+import net.sourceforge.pbeans.*
+// on *nix use: whotext = "who".execute().text
+whotext = '''
+gnat ttyp1 May 29 15:39 (coprolith.frii.com)
+bill ttyp1 May 28 15:38 (hilary.com)
+gnit ttyp1 May 27 15:37 (somewhere.org)
+'''
+
+class LoginInfo implements Persistent {
+ LoginInfo() {}
+ LoginInfo(name) { this.name = name; loginCount = 1 }
+ String name
+ int loginCount
+}
+
+def printAllUsers(store) {
+ printUsers(store, store.select(LoginInfo.class).collect{it.name}.sort())
+}
+
+def printUsers(store, list) {
+ list.each{
+ println "$it ${store.selectSingle(LoginInfo.class, 'name', it).loginCount}"
+ }
+}
+
+def addUsers(store) {
+ whotext.trim().split('\n').each{
+ m = it =~ /^(\S+)/
+ name = m[0][1]
+ item = store.selectSingle(LoginInfo.class, 'name', name)
+ if (item) {
+ item.loginCount++
+ store.save(item)
+ } else {
+ store.insert(new LoginInfo(name))
+ }
+ }
+}
+
+def ds = new org.hsqldb.jdbc.jdbcDataSource()
+ds.database = 'jdbc:hsqldb:hsql://localhost/mydb'
+ds.user = 'sa'
+ds.password = ''
+store = new Store(ds)
+if (args.size() == 0) {
+ addUsers(store)
+} else if (args == ['ALL']) {
+ printAllUsers(store)
+} else {
+ printUsers(store, args)
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_14.2
+//----------------------------------------------------------------------------------
+// Groovy would normally use JDBC here (see 14.1 for details)
+import com.sleepycat.je.*
+tx = null
+envHome = new File("D:/Projects/GroovyExamples/Pleac/data/db")
+
+myEnvConfig = new EnvironmentConfig()
+myEnvConfig.setAllowCreate(true)
+myEnv = new Environment(envHome, myEnvConfig)
+
+myDbConfig = new DatabaseConfig()
+myDbConfig.setAllowCreate(true)
+myDb = myEnv.openDatabase(tx, "vendorDB", myDbConfig)
+
+theKey = new DatabaseEntry("key".getBytes("UTF-8"))
+theData = new DatabaseEntry("data".getBytes("UTF-8"))
+myDb.put(tx, theKey, theData)
+myDb.close()
+// clear out database
+returnCount = true
+println myEnv.truncateDatabase(tx, "vendorDB", returnCount) + ' records deleted'
+// remove database
+myEnv.removeDatabase(tx, "vendorDB")
+myEnv.close()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_14.3
+//----------------------------------------------------------------------------------
+// Original cookbook example not likely in Groovy.
+// Here is a more realistic example, copying pbeans -> jdbc
+// Creation of pbeans database not strictly needed but shown for completion
+
+import net.sourceforge.pbeans.*
+import groovy.sql.Sql
+
+def ds = new org.hsqldb.jdbc.jdbcDataSource()
+ds.database = 'jdbc:hsqldb:hsql://localhost/mydb'
+ds.user = 'sa'
+ds.password = ''
+store = new Store(ds)
+
+class Person implements Persistent {
+ String name
+ String does
+ String email
+}
+
+// populate with test data
+store.insert(new Person(name:'Tom Christiansen', does:'book author', email:'tchrist@perl.com'))
+store.insert(new Person(name:'Tom Boutell', does:'Poet Programmer', email:'boutell@boutell.com'))
+
+people = store.select(Person.class)
+
+db = new Sql(ds)
+
+db.execute 'CREATE TABLE people ( name VARCHAR, does VARCHAR, email VARCHAR );'
+people.each{ p ->
+ db.execute "INSERT INTO people ( name, does, email ) VALUES ($p.name,$p.does,$p.email);"
+}
+db.eachRow("SELECT * FROM people where does like 'book%'"){
+ println "$it.name, $it.does, $it.email"
+}
+db.execute 'DROP TABLE people;'
+// => Tom Christiansen, book author, tchrist@perl.com
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_14.4
+//----------------------------------------------------------------------------------
+// Groovy would normally use JDBC here (see 14.1 for details)
+import com.sleepycat.je.*
+
+def copyEntries(indb, outdb) {
+ cursor = indb1.openCursor(null, null)
+ while (cursor.getNext(foundKey, foundData, LockMode.DEFAULT) == OperationStatus.SUCCESS)
+ outdb.out(tx, foundKey, foundData)
+ cursor.close()
+}
+
+tx = null
+envHome = new File("D:/Projects/GroovyExamples/Pleac/data/db")
+
+myEnvConfig = new EnvironmentConfig()
+myEnvConfig.setAllowCreate(true)
+myEnv = new Environment(envHome, myEnvConfig)
+
+myDbConfig = new DatabaseConfig()
+myDbConfig.setAllowCreate(true)
+indb1 = myEnv.openDatabase(tx, "db1", myDbConfig)
+indb2 = myEnv.openDatabase(tx, "db2", myDbConfig)
+outdb = myEnv.openDatabase(tx, "db3", myDbConfig)
+foundKey = new DatabaseEntry()
+foundData = new DatabaseEntry()
+copyEntries(indb1, outdb)
+copyEntries(indb2, outdb)
+cursor = indb2.openCursor(null, null)
+while (cursor.getNext(foundKey, foundData, LockMode.DEFAULT) == OperationStatus.SUCCESS)
+ outdb.out(tx, foundKey, foundData)
+cursor.close()
+indb1.close()
+indb2.close()
+outdb.close()
+myEnv.close()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_14.5
+//----------------------------------------------------------------------------------
+// If you are using a single file based persistence mechanism you can
+// use the file locking mechanisms mentioned in 7.11 otherwise the
+// database itself or the ORM layer will provide locking mechanisms.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_14.6
+//----------------------------------------------------------------------------------
+// N/A for most Java/Groovy persistent technologies.
+// Use indexes for RDBMS systems.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_14.7
+//----------------------------------------------------------------------------------
+ // We can write a category that allows the ArrayList class
+ // to be persisted as required.
+ class ArrayListCategory {
+ static file = new File('/temp.txt')
+ public static void save(ArrayList self) {
+ def LS = System.getProperty('line.separator')
+ file.withWriter{ w ->
+ self.each{ w.write(it + LS) }
+ }
+ }
+ }
+
+ lines = '''
+ zero
+ one
+ two
+ three
+ four
+ '''.trim().split('\n') as ArrayList
+
+ use(ArrayListCategory) {
+ println "ORIGINAL"
+ for (i in 0..<lines.size())
+ println "${i}: ${lines[i]}"
+
+ a = lines[-1]
+ lines[-1] = "last"
+ println "The last line was [$a]"
+
+ a = lines[0]
+ lines = ["first"] + lines[1..-1]
+ println "The first line was [$a]"
+
+ lines.add(3, 'Newbie')
+ lines.add(1, 'New One')
+
+ lines.remove(3)
+
+ println "REVERSE"
+ (lines.size() - 1).downto(0){ i ->
+ println "${i}: ${lines[i]}"
+ }
+ lines.save()
+ }
+ // =>
+ // ORIGINAL
+ // 0: zero
+ // 1: one
+ // 2: two
+ // 3: three
+ // 4: four
+ // The last line was [four]
+ // The first line was [zero]
+ // REVERSE
+ // 5: last
+ // 4: three
+ // 3: Newbie
+ // 2: one
+ // 1: New One
+ // 0: first
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_14.8
+//----------------------------------------------------------------------------------
+// example using pbeans
+import net.sourceforge.pbeans.*
+def ds = new org.hsqldb.jdbc.jdbcDataSource()
+ds.database = 'jdbc:hsqldb:hsql://localhost/mydb'
+ds.user = 'sa'
+ds.password = ''
+store = new Store(ds)
+
+class Person implements Persistent {
+ String name
+ String does
+ String email
+}
+
+name1 = 'Tom Christiansen'
+name2 = 'Tom Boutell'
+
+store.insert(new Person(name:name1, does:'book author', email:'tchrist@perl.com'))
+store.insert(new Person(name:name2, does:'shareware author', email:'boutell@boutell.com'))
+
+tom1 = store.selectSingle(Person.class, 'name', name1)
+tom2 = store.selectSingle(Person.class, 'name', name2)
+
+println "Two Toming: $tom1 $tom2"
+
+if (tom1.name == tom2.name && tom1.does == tom2.does && tom1.email == tom2.email)
+ println "You're having runtime fun with one Tom made two."
+else
+ println "No two Toms are ever alike"
+
+tom2.does = 'Poet Programmer'
+store.save(tom2)
+// =>
+// Two Toming: Person@12884e0 Person@8ab708
+// No two Toms are ever alike
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_14.9
+//----------------------------------------------------------------------------------
+// Use one of the mechanisms mentioned in 14.1 to load variables at the start
+// of the script and save them at the end. You can save the binding, individual
+// variables, maps of variables or composite objects.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_14.10
+//----------------------------------------------------------------------------------
+import groovy.sql.Sql
+
+users = ['20':'Joe Bloggs', '40':'Bill Clinton', '60':'Ben Franklin']
+
+def source = new org.hsqldb.jdbc.jdbcDataSource()
+source.database = 'jdbc:hsqldb:mem:PLEAC'
+source.user = 'sa'
+source.password = ''
+db = new Sql(source)
+
+db.execute 'CREATE TABLE users ( uid INT, login CHAR(8) );'
+users.each{ uid, login ->
+ db.execute "INSERT INTO users ( uid, login ) VALUES ($uid,$login);"
+}
+db.eachRow('SELECT uid, login FROM users WHERE uid < 50'){
+ println "$it.uid $it.login"
+}
+db.execute 'DROP TABLE users;'
+// =>
+// 20 Joe Bloggs
+// 40 Bill Clinton
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_14.11
+//----------------------------------------------------------------------------------
+// variation to cookbook: uses Firefox instead of Netscape, always assumes
+// argument is a regex, has some others args, retains no args to list all
+
+// uses jmork mork dbm reading library:
+// http://www.smartwerkz.com/projects/jmork/index.html
+import mork.*
+def cli = new CliBuilder()
+cli.h(longOpt: 'help', 'print this message')
+cli.e(longOpt: 'exclude', 'exclude hidden history entries (js, css, ads and images)')
+cli.c(longOpt: 'clean', 'clean off url query string when reporting urls')
+cli.v(longOpt: 'verbose', 'show referrer and first visit date')
+def options = cli.parse(args)
+if (options.h) { cli.usage(); System.exit(0) }
+regex = options.arguments()
+if (regex) regex = regex[0]
+reader = new FileReader('Pleac/data/history.dat')
+morkDocument = new MorkDocument(reader)
+tables = morkDocument.tables
+tables.each{ table ->
+ table.rows.each { row ->
+ url = row.getValue('URL')
+ if (options.c) url = url.tokenize('?')[0]
+ if (!regex || url =~ regex) {
+ if (!options.e || row.getValue('Hidden') != '1') {
+ println "$url\n Last Visited: ${date(row,'LastVisitDate')}"
+ if (options.v) {
+ println " First Visited: ${date(row,'FirstVisitDate')}"
+ println " Referrer: ${row.getValue('Referrer')}"
+ }
+ }
+ }
+ }
+}
+def date(row, key) {
+ return new Date((long)(row.getValue(key).toLong()/1000))
+}
+// $ groovy gfh -ev oracle' =>
+// http://www.oracle.com/technology/products/jdev/index.html
+// Last Visited: Thu Feb 15 20:20:36 EST 2007
+// First Visited: Thu Feb 15 20:20:36 EST 2007
+// Referrer: http://docs.codehaus.org/display/GROOVY/Oracle+JDeveloper+Plugin
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.1
+//----------------------------------------------------------------------------------
+// The are several Java options builder packages available. Some popular ones:
+// Apache Jakarta Commons CLI: http://jakarta.apache.org/commons/cli/
+// jopt-simple: http://jopt-simple.sourceforge.net
+// args4j: https://args4j.dev.java.net/ (requires Java 5 with annotations)
+// jargs: http://jargs.sourceforge.net/
+// te-code: http://te-code.sourceforge.net/article-20041121-cli.html
+// Most of these can be used from Groovy with some Groovy code benefits.
+// Groovy also has the CliBuilder built right in.
+
+
+// CliBuilder example
+def cli = new CliBuilder()
+cli.v(longOpt: 'verbose', 'verbose mode')
+cli.D(longOpt: 'Debug', 'display debug info')
+cli.o(longOpt: 'output', 'use/specify output file')
+def options = cli.parse(args)
+if (options.v) // ...
+if (options.D) println 'Debugging info available'
+if (options.o) {
+ println 'Output file flag was specified'
+ println "Output file is ${options.o}"
+}
+// ...
+
+
+// jopt-simple example 1 (short form)
+cli = new joptsimple.OptionParser("vDo::")
+options = cli.parse(args)
+if (options.wasDetected('o')) {
+ println 'Output file flag was specified.'
+ println "Output file is ${options.argumentsOf('o')}"
+}
+// ...
+
+
+// jopt-simple example 2 (declarative form)
+op = new joptsimple.OptionParser()
+VERBOSE = 'v'; op.accepts( VERBOSE, "verbose mode" )
+DEBUG = 'D'; op.accepts( DEBUG, "display debug info" )
+OUTPUT = 'o'; op.accepts( OUTPUT, "use/specify output file" ).withOptionalArg().
+ describedAs( "file" ).ofType( File.class )
+options = op.parse(args)
+params = options.nonOptionArguments()
+if (options.wasDetected( DEBUG )) println 'Debugging info available'
+// ...
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.2
+//----------------------------------------------------------------------------------
+// Groovy like Java can be run in a variety of scenarios, not just interactive vs
+// non-interative, e.g. within a servlet container. Sometimes InputStreams and other
+// mechanisms are used to hide away differences between the different containers
+// in which code is run; other times, code needs to be written purpose-built for
+// the container in which it is running. In most situations where the latter applies
+// the container will have specific lifecycle mechanisms to allow the code to
+// access specific needs, e.g. javax.servlet.ServletRequest.getInputStream()
+// rather than System.in
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.3
+//----------------------------------------------------------------------------------
+// Idiomatically Groovy encourages GUI over text-based applications where a rich
+// interface is desirable. Libraries for richer text-based interfaces include:
+// jline: http://jline.sourceforge.net
+// jcurses: http://sourceforge.net/projects/javacurses/
+// java-readline: http://java-readline.sourceforge.net
+// enigma console: http://sourceforge.net/projects/enigma-shell/
+// Note: Run examples using these libraries from command line not inside an IDE.
+
+// If you are using a terminal/console that understands ANSI codes
+// (excludes WinNT derivatives) you can just print the ANSI codes
+print ((char)27 + '[2J')
+
+// jline has constants for ANSI codes
+import jline.ANSIBuffer
+print ANSIBuffer.ANSICodes.clrscr()
+// Also available through ConsoleReader.clearScreen()
+
+// Using jcurses
+import jcurses.system.*
+bg = CharColor.BLACK
+fg = CharColor.WHITE
+screenColors = new CharColor(bg, fg)
+Toolkit.clearScreen(screenColors)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.4
+//----------------------------------------------------------------------------------
+// Not idiomatic for Groovy to use text-based applications here.
+
+// Using jcurses: http://sourceforge.net/projects/javacurses/
+// use Toolkit.screenWidth and Toolkit.screenHeight
+
+// 'barchart' example
+import jcurses.system.Toolkit
+numCols = Toolkit.screenWidth
+rand = new Random()
+if (numCols < 20) throw new RuntimeException("You must have at least 20 characters")
+values = (1..5).collect { rand.nextInt(20) } // generate rand values
+max = values.max()
+ratio = (numCols - 12)/max
+values.each{ i ->
+ printf('%8.1f %s\n', [i as double, "*" * ratio * i])
+}
+
+// gives, for example:
+// 15.0 *******************************
+// 10.0 *********************
+// 5.0 **********
+// 14.0 *****************************
+// 18.0 **************************************
+// Run from command line not inside an IDE which may give false width/height values.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.5
+//----------------------------------------------------------------------------------
+// Idiomatically Groovy encourages GUI over text-based applications where a rich
+// interface is desirable. See 15.3 for richer text-based interface libraries.
+// Note: Run examples using these libraries from command line not inside an IDE.
+
+// If you are using a terminal/console that understands ANSI codes
+// (excludes WinNT derivatives) you can just print the ANSI codes
+ESC = "${(char)27}"
+redOnBlack = ESC + '[31;40m'
+reset = ESC + '[0m'
+println (redOnBlack + 'Danger, Will Robinson!' + reset)
+
+// jline has constants for ANSI codes
+import jline.ANSIBuffer
+redOnBlack = ANSIBuffer.ANSICodes.attrib(31) + ANSIBuffer.ANSICodes.attrib(40)
+reset = ANSIBuffer.ANSICodes.attrib(0)
+println redOnBlack + 'Danger, Will Robinson!' + reset
+
+// Using JavaCurses
+import jcurses.system.*
+import jcurses.widgets.*
+whiteOnBlack = new CharColor(CharColor.BLACK, CharColor.WHITE)
+Toolkit.clearScreen(whiteOnBlack)
+redOnBlack = new CharColor(CharColor.BLACK, CharColor.RED)
+Toolkit.printString("Danger, Will Robinson!", 0, 0, redOnBlack)
+Toolkit.printString("This is just normal text.", 0, 1, whiteOnBlack)
+// Blink not supported by JavaCurses
+
+// Using jline constants for Blink
+blink = ANSIBuffer.ANSICodes.attrib(5)
+reset = ANSIBuffer.ANSICodes.attrib(0)
+println (blink + 'Do you hurt yet?' + reset)
+
+// Using jline constants for Coral snake rhyme
+def ansi(code) { ANSIBuffer.ANSICodes.attrib(code) }
+redOnBlack = ansi(31) + ansi(40)
+redOnYellow = ansi(31) + ansi(43)
+greenOnCyanBlink = ansi(32) + ansi(46) + ansi(5)
+reset = ansi(0)
+println redOnBlack + "venom lack"
+println redOnYellow + "kill that fellow"
+println greenOnCyanBlink + "garish!" + reset
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.6
+//----------------------------------------------------------------------------------
+// Default Java libraries buffer System.in by default.
+
+// Using JavaCurses:
+import jcurses.system.Toolkit
+print 'Press a key: '
+println "\nYou pressed the '${Toolkit.readCharacter().character}' key"
+
+// Also works for special keys:
+import jcurses.system.InputChar
+print "Press the 'End' key to finish: "
+ch = Toolkit.readCharacter()
+assert ch.isSpecialCode()
+assert ch.code == InputChar.KEY_END
+
+// See also jline Terminal#readCharacter() and Terminal#readVirtualKey()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.7
+//----------------------------------------------------------------------------------
+print "${(char)7}"
+
+// Using jline constant
+print "${jline.ConsoleOperations.KEYBOARD_BELL}"
+// Also available through ConsoleReader.beep()
+
+// Using JavaCurses (Works only with terminals that support 'beeps')
+import jcurses.system.Toolkit
+Toolkit.beep()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.8
+//----------------------------------------------------------------------------------
+// I think you would need to resort to platform specific calls here,
+// E.g. on *nix systems call 'stty' using execute().
+// Some things can be set through the packages mentioned in 15.3, e.g.
+// echo can be turned on and off, but others like setting the kill character
+// didn't appear to be supported (presumably because it doesn't make
+// sense for a cross-platform toolkit).
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.9
+//----------------------------------------------------------------------------------
+// Consider using Java's PushbackInputStream or PushbackReader
+// Different functionality to original cookbook but can be used
+// as an alternative for some scenarios.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.10
+//----------------------------------------------------------------------------------
+// If using Java 6, use Console.readPassword()
+// Otherwise use jline (use 0 instead of mask character '*' for no echo):
+password = new jline.ConsoleReader().readLine(new Character('*'))
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.11
+//----------------------------------------------------------------------------------
+// In Groovy (like Java) normal input is buffered so you can normally make
+// edits before hitting 'Enter'. For more control over editing (including completion
+// and history etc.) use one of the packages mentioned in 15.3, e.g. jline.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.12
+//----------------------------------------------------------------------------------
+// Use javacurses or jline (see 15.3) for low level screen management.
+// Java/Groovy would normally use a GUI for such functionality.
+
+// Here is a slight variation to cookbook example. This repeatedly calls
+// the command feedin on the command line, e.g. "cmd /c dir" on windows
+// or 'ps -aux' on Linux. Whenever a line changes, the old line is "faded
+// out" using font colors from white through to black. Then the new line
+// is faded in using the reverse process.
+import jcurses.system.*
+color = new CharColor(CharColor.BLACK, CharColor.WHITE)
+Toolkit.clearScreen(color)
+maxcol = Toolkit.screenWidth
+maxrow = Toolkit.screenHeight
+colors = [CharColor.WHITE, CharColor.CYAN, CharColor.YELLOW, CharColor.GREEN,
+ CharColor.RED, CharColor.BLUE, CharColor.MAGENTA, CharColor.BLACK]
+done = false
+refresh = false
+waittime = 8000
+oldlines = []
+def fade(line, row, colorList) {
+ for (i in 0..<colorList.size()) {
+ Toolkit.printString(line, 0, row, new CharColor(CharColor.BLACK, colorList[i]))
+ sleep 10
+ }
+}
+while(!done) {
+ if (waittime > 9999 || refresh) {
+ proc = args[0].execute()
+ lines = proc.text.split('\n')
+ for (r in 0..<maxrow) {
+ if (r >= lines.size() || r > oldlines.size() || lines[r] != oldlines[r]) {
+ if (oldlines != [])
+ fade(r < oldlines.size() ? oldlines[r] : ' ' * maxcol, r, colors)
+ fade(r < lines.size() ? lines[r] : ' ' * maxcol, r, colors.reverse())
+ }
+ }
+ oldlines = lines
+ refresh = false
+ waittime = 0
+ }
+ waittime += 200
+ sleep 200
+}
+
+// Keyboard handling would be similar to 15.6.
+// Something like below but need to synchronize as we are in different threads.
+Thread.start{
+ while(!done) {
+ ch = Toolkit.readCharacter()
+ if (ch.isSpecialCode() || ch.character == 'q') done = true
+ else refresh = true
+ }
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.13
+//----------------------------------------------------------------------------------
+// These examples uses expectj, a pure Java Expect-like module.
+// http://expectj.sourceforge.net/
+defaultTimeout = -1 // infinite
+expect = new expectj.ExpectJ("logfile.log", defaultTimeout)
+command = expect.spawn("program to run")
+command.expect('Password', 10)
+// expectj doesn't support regular expressions, but see readUntil
+// in recipe 18.6 for how to manually code this
+command.expect('invalid')
+command.send('Hello, world\r')
+// kill spawned process
+command.stop()
+
+// expecting multiple choices
+// expectj doesn't support multiple choices, but see readUntil
+// in recipe 18.6 for how to manually code this
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.14
+//----------------------------------------------------------------------------------
+// Methods not shown for the edit menu items, they would be the same as for the
+// file menu items.
+import groovy.swing.SwingBuilder
+def print() {}
+def save() {}
+frame = new SwingBuilder().frame(title:'Demo') {
+ menuBar {
+ menu(mnemonic:'F', 'File') {
+ menuItem (actionPerformed:this.&print, 'Print')
+ separator()
+ menuItem (actionPerformed:this.&save, 'Save')
+ menuItem (actionPerformed:{System.exit(0)}, 'Quit immediately')
+ }
+ menu(mnemonic:'O', 'Options') {
+ checkBoxMenuItem ('Create Debugging Info', state:true)
+ }
+ menu(mnemonic:'D', 'Debug') {
+ group = buttonGroup()
+ radioButtonMenuItem ('Log Level 1', buttonGroup:group, selected:true)
+ radioButtonMenuItem ('Log Level 2', buttonGroup:group)
+ radioButtonMenuItem ('Log Level 3', buttonGroup:group)
+ }
+ menu(mnemonic:'F', 'Format') {
+ menu('Font') {
+ group = buttonGroup()
+ radioButtonMenuItem ('Times Roman', buttonGroup:group, selected:true)
+ radioButtonMenuItem ('Courier', buttonGroup:group)
+ }
+ }
+ menu(mnemonic:'E', 'Edit') {
+ menuItem (actionPerformed:{}, 'Copy')
+ menuItem (actionPerformed:{}, 'Cut')
+ menuItem (actionPerformed:{}, 'Paste')
+ menuItem (actionPerformed:{}, 'Delete')
+ separator()
+ menu('Object ...') {
+ menuItem (actionPerformed:{}, 'Circle')
+ menuItem (actionPerformed:{}, 'Square')
+ menuItem (actionPerformed:{}, 'Point')
+ }
+ }
+ }
+}
+frame.pack()
+frame.show()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.15
+//----------------------------------------------------------------------------------
+// Registration Example
+import groovy.swing.SwingBuilder
+def cancel(event) {
+ println 'Sorry you decided not to register.'
+ dialog.dispose()
+}
+def register(event) {
+ if (swing.name?.text) {
+ println "Welcome to the fold $swing.name.text"
+ dialog.dispose()
+ } else println "You didn't give me your name!"
+}
+def dialog(event) {
+ dialog = swing.createDialog(title:'Entry')
+ def panel = swing.panel {
+ vbox {
+ hbox {
+ label(text:'Name')
+ textField(columns:20, id:'name')
+ }
+ hbox {
+ button('Register', actionPerformed:this.&register)
+ button('Cancel', actionPerformed:this.&cancel)
+ }
+ }
+ }
+ dialog.getContentPane().add(panel)
+ dialog.pack()
+ dialog.show()
+}
+swing = new SwingBuilder()
+frame = swing.frame(title:'Registration Example') {
+ panel {
+ button(actionPerformed:this.&dialog, 'Click Here For Registration Form')
+ glue()
+ button(actionPerformed:{System.exit(0)}, 'Quit')
+ }
+}
+frame.pack()
+frame.show()
+
+
+// Error Example, slight variation to original cookbook
+import groovy.swing.SwingBuilder
+import javax.swing.WindowConstants as WC
+import javax.swing.JOptionPane
+def calculate(event) {
+ try {
+ swing.result.text = evaluate(swing.expr.text)
+ } catch (Exception ex) {
+ JOptionPane.showMessageDialog(frame, ex.message)
+ }
+}
+swing = new SwingBuilder()
+frame = swing.frame(title:'Calculator Example',
+ defaultCloseOperation:WC.EXIT_ON_CLOSE) {
+ panel {
+ vbox {
+ hbox {
+ label(text:'Expression')
+ hstrut()
+ textField(columns:12, id:'expr')
+ }
+ hbox {
+ label(text:'Result')
+ glue()
+ label(id:'result')
+ }
+ hbox {
+ button('Calculate', actionPerformed:this.&calculate)
+ button('Quit', actionPerformed:{System.exit(0)})
+ }
+ }
+ }
+}
+frame.pack()
+frame.show()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.16
+//----------------------------------------------------------------------------------
+// Resizing in Groovy follows Java rules, i.e. is dependent on the layout manager.
+// You can set preferred, minimum and maximum sizes (may be ignored by some layout managers).
+// You can setResizable(false) for some components.
+// You can specify a weight value for some layout managers, e.g. GridBagLayout
+// which control the degree of scaling which occurs during resizing.
+// Some layout managers, e.g. GridLayout, automaticaly resize their contained widgets.
+// You can capture resize events and do everything manually yourself.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.17
+//----------------------------------------------------------------------------------
+// Removing DOS console on Windows:
+// If you are using java.exe to start your Groovy script, use javaw.exe instead.
+// If you are using groovy.exe to start your Groovy script, use groovyw.exe instead.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.18
+//----------------------------------------------------------------------------------
+// additions to original cookbook:
+// random starting position
+// color changes after each bounce
+import jcurses.system.*
+color = new CharColor(CharColor.BLACK, CharColor.WHITE)
+Toolkit.clearScreen(color)
+rand = new Random()
+maxrow = Toolkit.screenWidth
+maxcol = Toolkit.screenHeight
+rowinc = 1
+colinc = 1
+row = rand.nextInt(maxrow)
+col = rand.nextInt(maxcol)
+chars = '*-/|\\_'
+colors = [CharColor.RED, CharColor.BLUE, CharColor.YELLOW,
+ CharColor.GREEN, CharColor.CYAN, CharColor.MAGENTA]
+delay = 20
+ch = null
+def nextChar(){
+ ch = chars[0]
+ chars = chars[1..-1] + chars[0]
+ color = new CharColor(CharColor.BLACK, colors[0])
+ colors = colors[1..-1] + colors[0]
+}
+nextChar()
+while(true) {
+ Toolkit.printString(ch, row, col, color)
+ sleep delay
+ row = row + rowinc
+ col = col + colinc
+ if (row in [0, maxrow]) { nextChar(); rowinc = -rowinc }
+ if (col in [0, maxcol]) { nextChar(); colinc = -colinc }
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_15.19
+//----------------------------------------------------------------------------------
+// Variation to cookbook. Let's you reshuffle lines in a multi-line string
+// by drag-n-drop.
+import java.awt.*
+import java.awt.datatransfer.*
+import java.awt.dnd.*
+import javax.swing.*
+import javax.swing.ScrollPaneConstants as SPC
+
+class DragDropList extends JList implements
+ DragSourceListener, DropTargetListener, DragGestureListener {
+ def dragSource
+ def dropTarget
+ def dropTargetCell
+ int draggedIndex = -1
+ def localDataFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType)
+ def supportedFlavors = [localDataFlavor] as DataFlavor[]
+
+ public DragDropList(model) {
+ super()
+ setModel(model)
+ setCellRenderer(new DragDropCellRenderer(this))
+ dragSource = new DragSource()
+ dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this)
+ dropTarget = new DropTarget(this, this)
+ }
+
+ public void dragGestureRecognized(DragGestureEvent dge) {
+ int index = locationToIndex(dge.dragOrigin)
+ if (index == -1 || index == model.size() - 1) return
+ def trans = new CustomTransferable(model.getElementAt(index), this)
+ draggedIndex = index
+ dragSource.startDrag(dge, Cursor.defaultCursor, trans, this)
+ }
+
+ public void dragDropEnd(DragSourceDropEvent dsde) {
+ dropTargetCell = null
+ draggedIndex = -1
+ repaint()
+ }
+
+ public void dragEnter(DragSourceDragEvent dsde) { }
+
+ public void dragExit(DragSourceEvent dse) { }
+
+ public void dragOver(DragSourceDragEvent dsde) { }
+
+ public void dropActionChanged(DragSourceDragEvent dsde) { }
+
+ public void dropActionChanged(DropTargetDragEvent dtde) { }
+
+ public void dragExit(DropTargetEvent dte) { }
+
+ public void dragEnter(DropTargetDragEvent dtde) {
+ if (dtde.source != dropTarget) dtde.rejectDrag()
+ else dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE)
+ }
+
+ public void dragOver(DropTargetDragEvent dtde) {
+ if (dtde.source != dropTarget) dtde.rejectDrag()
+ int index = locationToIndex(dtde.location)
+ if (index == -1 || index == draggedIndex + 1) dropTargetCell = null
+ else dropTargetCell = model.getElementAt(index)
+ repaint()
+ }
+
+ public void drop(DropTargetDropEvent dtde) {
+ if (dtde.source != dropTarget) {
+ dtde.rejectDrop()
+ return
+ }
+ int index = locationToIndex(dtde.location)
+ if (index == -1 || index == draggedIndex) {
+ dtde.rejectDrop()
+ return
+ }
+ dtde.acceptDrop(DnDConstants.ACTION_MOVE)
+ def dragged = dtde.transferable.getTransferData(localDataFlavor)
+ boolean sourceBeforeTarget = (draggedIndex < index)
+ model.remove(draggedIndex)
+ model.add((sourceBeforeTarget ? index - 1 : index), dragged)
+ dtde.dropComplete(true)
+ }
+}
+
+class CustomTransferable implements Transferable {
+ def object
+ def ddlist
+
+ public CustomTransferable(object, ddlist) {
+ this.object = object
+ this.ddlist = ddlist
+ }
+
+ public Object getTransferData(DataFlavor df) {
+ if (isDataFlavorSupported(df)) return object
+ }
+
+ public boolean isDataFlavorSupported(DataFlavor df) {
+ return df.equals(ddlist.localDataFlavor)
+ }
+
+ public DataFlavor[] getTransferDataFlavors() {
+ return ddlist.supportedFlavors
+ }
+}
+
+class DragDropCellRenderer extends DefaultListCellRenderer {
+ boolean isTargetCell
+ def ddlist
+
+ public DragDropCellRenderer(ddlist) {
+ super()
+ this.ddlist = ddlist
+ }
+
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index, boolean isSelected, boolean hasFocus) {
+ isTargetCell = (value == ddlist.dropTargetCell)
+ boolean showSelected = isSelected && !isTargetCell
+ return super.getListCellRendererComponent(list, value, index, showSelected, hasFocus)
+ }
+
+ public void paintComponent(Graphics g) {
+ super.paintComponent(g)
+ if (isTargetCell) {
+ g.setColor(Color.black)
+ g.drawLine(0, 0, size.width.intValue(), 0)
+ }
+ }
+}
+
+lines = '''
+This is line 1
+This is line 2
+This is line 3
+This is line 4
+'''.trim().split('\n')
+def listModel = new DefaultListModel()
+lines.each{ listModel.addElement(it) }
+listModel.addElement(' ') // dummy
+def list = new DragDropList(listModel)
+def sp = new JScrollPane(list, SPC.VERTICAL_SCROLLBAR_ALWAYS, SPC.HORIZONTAL_SCROLLBAR_NEVER)
+def frame = new JFrame('Line Shuffle Example')
+frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
+frame.contentPane.add(sp)
+frame.pack()
+frame.setVisible(true)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.1
+//----------------------------------------------------------------------------------
+output = "program args".execute().text
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.2
+//----------------------------------------------------------------------------------
+proc = "vi myfile".execute()
+proc.waitFor()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.3
+//----------------------------------------------------------------------------------
+// Calling execute() on a String, String[] or List (of Strings or objects with
+// a toString() method) will fork off another process.
+// This doesn't replace the existing process but if you simply finish the original
+// process (leaving the spawned process to finish asynchronously) you will achieve
+// a similar thing.
+"archive *.data".execute()
+["archive", "accounting.data"].execute()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.4
+//----------------------------------------------------------------------------------
+// sending text to the input of another process
+proc = 'groovy -e "print System.in.text.toUpperCase()"'.execute()
+Thread.start{
+ def writer = new PrintWriter(new BufferedOutputStream(proc.out))
+ writer.println('Hello')
+ writer.close()
+}
+proc.waitFor()
+// further process output from process
+print proc.text.reverse()
+// =>
+// OLLEH
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.5
+//----------------------------------------------------------------------------------
+// filter your own output
+keep = System.out
+pipe = new PipedInputStream()
+reader = new BufferedReader(new InputStreamReader(pipe))
+System.setOut(new PrintStream(new BufferedOutputStream(new PipedOutputStream(pipe))))
+int numlines = 2
+Thread.start{
+ while((next = reader.readLine()) != null) {
+ if (numlines-- > 0) keep.println(next)
+ }
+}
+(1..8).each{ println it }
+System.out.close()
+System.setOut(keep)
+(9..10).each{ println it }
+// =>
+// 1
+// 2
+// 9
+// 10
+
+
+// filtering output by adding quotes and numbers
+class FilterOutput extends Thread {
+ Closure c
+ Reader reader
+ PrintStream orig
+ FilterOutput(Closure c) {
+ this.c = c
+ orig = System.out
+ def pipe = new PipedInputStream()
+ reader = new BufferedReader(new InputStreamReader(pipe))
+ System.setOut(new PrintStream(new BufferedOutputStream(new PipedOutputStream(pipe))))
+ }
+ void run() {
+ def next
+ while((next = reader.readLine()) != null) {
+ c(orig, next)
+ }
+ }
+ def close() {
+ sleep 100
+ System.out.close()
+ System.setOut(orig)
+ }
+}
+cnt = 0
+number = { s, n -> cnt++; s.println(cnt + ':' + n) }
+quote = { s, n -> s.println('> ' + n) }
+f1 = new FilterOutput(number); f1.start()
+f2 = new FilterOutput(quote); f2.start()
+('a'..'e').each{ println it }
+f2.close()
+f1.close()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.6
+//----------------------------------------------------------------------------------
+// Groovy programs (like Java ones) would use streams here. Just process
+// another stream instead of System.in or System.out:
+
+// process url text
+input = new URL(address).openStream()
+// ... process 'input' stream
+
+// process compressed file
+input = new GZIPInputStream(new FileInputStream('source.gzip'))
+// ... process 'input' stream
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.7
+//----------------------------------------------------------------------------------
+// To read STDERR of a process you execute
+proc = 'groovy -e "println args[0]"'.execute()
+proc.waitFor()
+println proc.err.text
+// => Caught: java.lang.ArrayIndexOutOfBoundsException: 0 ...
+
+// To redirect your STDERR to a file
+System.setErr(new PrintStream(new FileOutputStream("error.txt")))
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.8
+//----------------------------------------------------------------------------------
+// See 16.2, the technique allows both STDIN and STDOUT of another program to be
+// changed at the same time, not just one or the other as per Perl 16.2 solution
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.9
+//----------------------------------------------------------------------------------
+// See 16.2 and 16.7, the techniques can be combined to allow all three streams
+// (STDIN, STDOUT, STDERR) to be altered as required.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.10
+//----------------------------------------------------------------------------------
+// Groovy can piggy-back on the many options available to Java here:
+// JIPC provides a wide set of standard primitives: semaphore, event,
+// FIFO queue, barrier, shared memory, shared and exclusive locks:
+// http://www.garret.ru/~knizhnik/jipc/jipc.html
+// sockets allow process to communicate via low-level packets
+// CORBA, RMI, SOAP allow process to communicate via RPC calls
+// shared files can also be used
+// JMS allows process to communicate via a messaging service
+
+// Simplist approach is to just link streams:
+proc1 = 'groovy -e "println args[0]" Hello'.execute()
+proc2 = 'groovy -e "print System.in.text.toUpperCase()"'.execute()
+Thread.start{
+ def reader = new BufferedReader(new InputStreamReader(proc1.in))
+ def writer = new PrintWriter(new BufferedOutputStream(proc2.out))
+ while ((next = reader.readLine()) != null) {
+ writer.println(next)
+ }
+ writer.close()
+}
+proc2.waitFor()
+print proc2.text
+// => HELLO
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.11
+//----------------------------------------------------------------------------------
+// Java/Groovy would normally just use some socket-based technique for communicating
+// between processes (see 16.10 for a list of options). If you really must use a named
+// pipe, you have these options:
+// (1) On *nix machines:
+// * Create a named pipe by invoking the mkfifo utility using execute().
+// * Open a named pipe by name - which is just like opening a file.
+// * Run an external process setting its input and output streams (see 16.1, 16.4, 16.5)
+// (2) On Windows machines, Using JCIFS to Connect to Win32 Named Pipes, see:
+// http://jcifs.samba.org/src/docs/pipes.html
+// Neither of these achieve exactly the same result as the Perl example but some
+// scenarios will be almost identical.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.12
+//----------------------------------------------------------------------------------
+// The comments made in 16.10 regarding other alternative IPC mechanisms also apply here.
+
+// This example would normally be done with multiple threads in Java/Groovy as follows.
+class Shared {
+ String buffer = "not set yet"
+ synchronized void leftShift(value){
+ buffer = value
+ notifyAll()
+ }
+ synchronized Object read() {
+ return buffer
+ }
+}
+shared = new Shared()
+rand = new Random()
+threads = []
+(1..5).each{
+ t = new Thread(){
+ def me = t
+ for (j in 0..9) {
+ shared << "$me.name $j"
+ sleep 100 + rand.nextInt(200)
+ }
+ }
+ t.start()
+}
+while(1) {
+ println shared.read()
+ sleep 50
+}
+// =>
+// not set yet
+// Thread-2 0
+// Thread-5 1
+// Thread-1 1
+// Thread-4 2
+// Thread-3 1
+// ...
+// Thread-5 9
+
+
+// Using JIPC between processes (as a less Groovy alternative that is closer
+// to the original cookbook) is shown below.
+
+// ipcWriterScript:
+import org.garret.jipc.client.JIPCClientFactory
+port = 6000
+factory = JIPCClientFactory.instance
+session = factory.create('localhost', port)
+mutex = session.createMutex("myMutex", false)
+buffer = session.createSharedMemory("myBuffer", "not yet set")
+name = args[0]
+rand = new Random()
+(0..99).each {
+ mutex.lock()
+ buffer.set("$name $it".toString())
+ mutex.unlock()
+ sleep 200 + rand.nextInt(500)
+}
+session.close()
+
+// ipcReaderScript:
+import org.garret.jipc.client.JIPCClientFactory
+port = 6000
+factory = JIPCClientFactory.instance
+session = factory.create('localhost', port)
+mutex = session.createMutex("myMutex", false)
+buffer = session.createSharedMemory("myBuffer", "not yet set")
+rand = new Random()
+(0..299).each {
+ mutex.lock()
+ println buffer.get()
+ mutex.unlock()
+ sleep 150
+}
+session.close()
+
+// kick off processes:
+"java org.garret.jipc.server.JIPCServer 6000".execute()
+"groovy ipcReaderScript".execute()
+(0..3).each{ "groovy ipcWriterScript $it".execute() }
+
+// =>
+// ...
+// 0 10
+// 2 10
+// 2 11
+// 1 9
+// 1 9
+// 1 10
+// 2 12
+// 3 12
+// 3 12
+// 2 13
+// ...
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.13
+//----------------------------------------------------------------------------------
+// Signal handling in Groovy (like Java) is operating system and JVM dependent.
+// The ISO C standard only requires the signal names SIGABRT, SIGFPE, SIGILL,
+// SIGINT, SIGSEGV, and SIGTERM to be defined but depending on your platform
+// other signals may be present, e.g. Windows supports SIGBREAK. For more info
+// see: http://www-128.ibm.com/developerworks/java/library/i-signalhandling/
+// Note: if you start up the JVM with -Xrs the JVM will try to reduce its
+// internal usage of signals. Also the JVM takes over meany hooks and provides
+// platform independent alternatives, e.g. see java.lang.Runtime#addShutdownHook()
+
+// To see what signals are available for your system (excludes ones taken over
+// by the JVM):
+sigs = '''HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM
+USR1 USR2 CHLD PWR WINCH URG POLL STOP TSTP CONT TTIN TTOU VTALRM PROF XCPU
+XFSZ WAITING LWP AIO IO INFO THR BREAK FREEZE THAW CANCEL EMT
+'''
+
+sigs.tokenize(' \n').each{
+ try {
+ print ' ' + new sun.misc.Signal(it)
+ } catch(IllegalArgumentException iae) {}
+}
+// => on Windows XP:
+// SIGINT SIGILL SIGABRT SIGFPE SIGSEGV SIGTERM SIGBREAK
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.14
+//----------------------------------------------------------------------------------
+// To send a signal to your process:
+Signal.raise(new Signal("INT"))
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.15
+//----------------------------------------------------------------------------------
+// install a signal handler
+class DiagSignalHandler implements SignalHandler { ... }
+diagHandler = new DiagSignalHandler()
+Signal.handle(new Signal("INT"), diagHandler)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.16
+//----------------------------------------------------------------------------------
+// temporarily install a signal handler
+class DiagSignalHandler implements SignalHandler { ... }
+diagHandler = new DiagSignalHandler()
+oldHandler = Signal.handle(new Signal("INT"), diagHandler)
+Signal.handle(new Signal("INT"), oldHandler)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.17
+//----------------------------------------------------------------------------------
+import sun.misc.Signal
+import sun.misc.SignalHandler
+
+class DiagSignalHandler implements SignalHandler {
+ private oldHandler
+
+ // Static method to install the signal handler
+ public static install(signal) {
+ def diagHandler = new DiagSignalHandler()
+ diagHandler.oldHandler = Signal.handle(signal, diagHandler)
+ }
+
+ public void handle(Signal sig) {
+ println("Diagnostic Signal handler called for signal "+sig)
+ // Output information for each thread
+ def list = []
+ Thread.activeCount().each{ list += null }
+ Thread[] threadArray = list as Thread[]
+ int numThreads = Thread.enumerate(threadArray)
+ println("Current threads:")
+ for (i in 0..<numThreads) {
+ println(" "+threadArray[i])
+ }
+
+ // Chain back to previous handler, if one exists
+ if ( oldHandler != SIG_DFL && oldHandler != SIG_IGN ) {
+ oldHandler.handle(sig)
+ }
+ }
+}
+// install using:
+DiagSignalHandler.install(new Signal("INT"))
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.18
+//----------------------------------------------------------------------------------
+// See 16.17, just don't chain to the previous handler because the default handler
+// will abort the process.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.19
+//----------------------------------------------------------------------------------
+// Groovy relies on Java features here. Java doesn't keep the process around
+// as it stores metadata in a Process object. You can call waitFor() or destroy()
+// or exitValue() on the Process object. If the Process object is garbage collected,
+// the process can still execute asynchronously with respect to the original process.
+
+// For ensuring processes don't die, see:
+// http://jakarta.apache.org/commons/daemon/
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.20
+//----------------------------------------------------------------------------------
+// There is no equivalent to a signal mask available directly in Groovy or Java.
+// You can override and ignore individual signals using recipes 16.16 - 16.18.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.21
+//----------------------------------------------------------------------------------
+t = new Timer()
+t.runAfter(3500){
+ println 'Took too long'
+ System.exit(1)
+}
+def count = 0
+6.times{
+ count++
+ sleep 1000
+ println "Count = $count"
+}
+t.cancel()
+// See also special JMX timer class: javax.management.timer.Timer
+// For an external process you can also use: proc.waitForOrKill(3500)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_16.22
+//----------------------------------------------------------------------------------
+// One way to implement this functionality is to automatically replace the ~/.plan
+// etc. files every fixed timed interval - though this wouldn't be efficient.
+// Here is a simplified version which is a simplified version compared to the
+// original cookbook. It only looks at the ~/.signature file and changes it
+// freely. It also doesn't consider other news reader related files.
+
+def sigs = '''
+Make is like Pascal: everybody likes it, so they go in and change it.
+--Dennis Ritchie
+%%
+I eschew embedded capital letters in names; to my prose-oriented eyes,
+they are too awkward to read comfortably. They jangle like bad typography.
+--Rob Pike
+%%
+God made the integers; all else is the work of Man.
+--Kronecker
+%%
+I d rather have :rofix than const. --Dennis Ritchie
+%%
+If you want to program in C, program in C. It s a nice language.
+I use it occasionally... :-) --Larry Wall
+%%
+Twisted cleverness is my only skill as a programmer.
+--Elizabeth Zwicky
+%%
+Basically, avoid comments. If your code needs a comment to be understood,
+it would be better to rewrite it so it s easier to understand.
+--Rob Pike
+%%
+Comments on data are usually much more helpful than on algorithms.
+--Rob Pike
+%%
+Programs that write programs are the happiest programs in the world.
+--Andrew Hume
+'''.trim().split(/\n%%\n/)
+name = 'me@somewhere.org\n'
+file = new File(System.getProperty('user.home') + File.separator + '.signature')
+rand = new Random()
+while(1) {
+ file.delete()
+ file << name + sigs[rand.nextInt(sigs.size())]
+ sleep 10000
+}
+
+// Another way to implement this functionality (in a completely different way to the
+// original cookbook) is to use a FileWatcher class, e.g.
+// http://www.rgagnon.com/javadetails/java-0490.html (FileWatcher and DirWatcher)
+// http://www.jconfig.org/javadoc/org/jconfig/FileWatcher.html
+
+// These file watchers notify us whenever the file is modified, see Pleac chapter 7
+// for workarounds to not being able to get last accessed time vs last modified time.
+// (We would now need to touch the file whenever we accessed it to make it change).
+// Our handler called from the watchdog class would update the file contents.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.0
+//----------------------------------------------------------------------------------
+myClient = new Socket("Machine name", portNumber)
+myAddress = myClient.inetAddress
+myAddress.hostAddress // string representation of host address
+myAddress.hostName // host name
+myAddress.address // IP address as array of bytes
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.1
+//----------------------------------------------------------------------------------
+s = new Socket("localhost", 5000);
+s << "Why don't you call me anymore?\n"
+s.close()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.2
+//----------------------------------------------------------------------------------
+// commandline socket server echoing input back to originator
+groovy -l 5000 -e "println line"
+
+// commandline socket server eching input to stderr
+groovy -l 5000 -e "System.err.println line"
+
+// a web server as a script (extension to cookbook)
+ server = new ServerSocket(5000)
+ while(true) {
+ server.accept() { socket ->
+ socket.withStreams { input, output ->
+ // ignore input and just serve dummy content
+ output.withWriter { writer ->
+ writer << "HTTP/1.1 200 OK\n"
+ writer << "Content-Type: text/html\n\n"
+ writer << "<html><body>Hello World! It's ${new Date()}</body></html>\n"
+ }
+ }
+ }
+ }
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.3
+//----------------------------------------------------------------------------------
+server = new ServerSocket(5000)
+while(true) {
+ server.accept() { socket ->
+ socket.withStreams { input, output ->
+ w = new PrintWriter(output)
+ w << "What is your name? "
+ w.flush()
+ r = input.readLine()
+ System.err.println "User responded with $r"
+ w.close()
+ }
+ }
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.4
+//----------------------------------------------------------------------------------
+// UDP client
+data = "Message".getBytes("ASCII")
+addr = InetAddress.getByName("localhost")
+port = 5000
+packet = new DatagramPacket(data, data.length, addr, port)
+socket = new DatagramSocket()
+socket.send(packet)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.5
+//----------------------------------------------------------------------------------
+// UDP server
+socket = new DatagramSocket(5000)
+buffer = (' ' * 4096) as byte[]
+while(true) {
+ incoming = new DatagramPacket(buffer, buffer.length)
+ socket.receive(incoming)
+ s = new String(incoming.data, 0, incoming.length)
+ String reply = "Client said: '$s'"
+ outgoing = new DatagramPacket(reply.bytes, reply.size(),
+ incoming.address, incoming.port);
+ socket.send(outgoing)
+}
+
+// UDP client
+data = "Original Message".getBytes("ASCII")
+addr = InetAddress.getByName("localhost")
+port = 5000
+packet = new DatagramPacket(data, data.length, addr, port)
+socket = new DatagramSocket()
+socket.send(packet)
+socket.setSoTimeout(30000) // block for no more than 30 seconds
+buffer = (' ' * 4096) as byte[]
+response = new DatagramPacket(buffer, buffer.length)
+socket.receive(response)
+s = new String(response.data, 0, response.length)
+println "Server said: '$s'"
+// => Server said: 'Client said: 'Original Message''
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.6
+//----------------------------------------------------------------------------------
+// DOMAIN sockets not available in cross platform form.
+// On Linux, use jbuds:
+// http://www.graphixprose.com/jbuds/
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.7
+//----------------------------------------------------------------------------------
+// TCP socket
+socketAddress = tcpSocket.remoteSocketAddress
+println "$socketAddress.address, $socketAddress.hostName, $socketAddress.port"
+// UDP packet
+println "$udpPacket.address, $udpPacket.port"
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.8
+//----------------------------------------------------------------------------------
+// Print the fully qualified domain name for this IP address
+println InetAddress.localHost.canonicalHostName
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.9
+//----------------------------------------------------------------------------------
+socket.shutdownInput()
+socket.shutdownOutput()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.10
+//----------------------------------------------------------------------------------
+// Spawn off a thread to handle each direction
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.11
+//----------------------------------------------------------------------------------
+// Spawn off a thread to handle each request.
+// This is done automatically by the Groovy accept() method on ServerSocket.
+// See 17.3 for an example.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.12
+//----------------------------------------------------------------------------------
+// Use a thread pool
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.13
+//----------------------------------------------------------------------------------
+// Consider using Selector and/or SocketChannel, ServerSocketChannel and DatagramChannel
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.14
+//----------------------------------------------------------------------------------
+// When creating a socket on a multihomed machine, use the socket constructor with
+// 4 params to select a specific address from those available:
+socket = new Socket(remoteAddr, remotePort, localAddr, localPort)
+
+// When creating a server on a multihomed machine supply the optional bindAddr param:
+new ServerSocket(port, queueLength, bindAddr)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.15
+//----------------------------------------------------------------------------------
+// Fork off a thread for your server and call setDaemon(true) on the thread.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.16
+//----------------------------------------------------------------------------------
+// Consider using special packages designed to provide robust startup/shutdown
+// capability, e.g.: http://jakarta.apache.org/commons/daemon/
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.17
+//----------------------------------------------------------------------------------
+// Alternative to cookbook as proposed inetd solution is not cross platform.
+host = 'localhost'
+for (port in 1..1024) {
+ try {
+ s = new Socket(host, port)
+ println("There is a server on port $port of $host")
+ }
+ catch (Exception ex) {}
+}
+// You could open a ServerSocket() on each unused port and monitor those.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_17.18
+//----------------------------------------------------------------------------------
+// It's not too hard to write a TCP Proxy in Groovy but numerous Java packages
+// already exist, so we might as well use one of those:
+// http://ws.apache.org/axis/java/user-guide.html#AppendixUsingTheAxisTCPMonitorTcpmon
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_18.1
+//----------------------------------------------------------------------------------
+name = 'www.perl.com'
+addresses = InetAddress.getAllByName(name)
+println addresses // => {www.perl.com/208.201.239.36, www.perl.com/208.201.239.37}
+// or to just resolve one:
+println InetAddress.getByName(name) // => www.perl.com/208.201.239.36
+// try a different address
+name = 'groovy.codehaus.org'
+addresses = InetAddress.getAllByName(name)
+println addresses // => {groovy.codehaus.org/63.246.7.187}
+// starting with IP address
+address = InetAddress.getByAddress([208, 201, 239, 36] as byte[])
+println address.hostName // => www.oreillynet.com
+
+// For more complex operations use dnsjava: http://www.dnsjava.org/
+import org.xbill.DNS.*
+System.setProperty("sun.net.spi.nameservice.provider.1","dns,dnsjava")
+Lookup lookup = new Lookup('cnn.com', Type.ANY)
+records = lookup.run()
+println "${records?.size()} record(s) found"
+records.each{ println it }
+// =>
+// 17 record(s) found
+// cnn.com. 55 IN A 64.236.16.20
+// cnn.com. 55 IN A 64.236.16.52
+// cnn.com. 55 IN A 64.236.16.84
+// cnn.com. 55 IN A 64.236.16.116
+// cnn.com. 55 IN A 64.236.24.12
+// cnn.com. 55 IN A 64.236.24.20
+// cnn.com. 55 IN A 64.236.24.28
+// cnn.com. 55 IN A 64.236.29.120
+// cnn.com. 324 IN NS twdns-02.ns.aol.com.
+// cnn.com. 324 IN NS twdns-03.ns.aol.com.
+// cnn.com. 324 IN NS twdns-04.ns.aol.com.
+// cnn.com. 324 IN NS twdns-01.ns.aol.com.
+// cnn.com. 3324 IN SOA twdns-01.ns.aol.com. hostmaster.tbsnames.turner.com. 2007011203 900 300 604801 900
+// cnn.com. 3324 IN MX 10 atlmail3.turner.com.
+// cnn.com. 3324 IN MX 10 atlmail5.turner.com.
+// cnn.com. 3324 IN MX 20 nycmail2.turner.com.
+// cnn.com. 3324 IN MX 30 nycmail1.turner.com.
+
+// faster reverse lookup using dnsjava
+def reverseDns(hostIp) {
+ name = ReverseMap.fromAddress(hostIp)
+ rec = Record.newRecord(name, Type.PTR, DClass.IN)
+ query = Message.newQuery(rec)
+ response = new ExtendedResolver().send(query)
+ answers = response.getSectionArray(Section.ANSWER)
+ if (answers) return answers[0].rdataToString() else return hostIp
+}
+println '208.201.239.36 => ' + reverseDns('208.201.239.36')
+// => 208.201.239.36 => www.oreillynet.com.
+
+def hostAddrs(name) {
+ addresses = Address.getAllByName(name)
+ println addresses[0].canonicalHostName + ' => ' + addresses.collect{ it.hostAddress }.join(' ')
+}
+hostAddrs('www.ora.com')
+// => www.oreillynet.com. => 208.201.239.36 208.201.239.37
+hostAddrs('www.whitehouse.gov')
+// => 61.9.209.153 => 61.9.209.153 61.9.209.151
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_18.2
+//----------------------------------------------------------------------------------
+// commons net examples (explicit error handling not shown)
+import java.text.DateFormat
+import org.apache.commons.net.ftp.FTPClient
+// connect
+server = "localhost" //server = "ftp.host.com"
+
+ftp = new FTPClient()
+ftp.connect( server )
+ftp.login( 'anonymous', 'guest' ) //ftp.login( 'username', 'password' )
+
+println "Connected to $server. $ftp.replyString"
+
+// retrieve file
+ftp.changeWorkingDirectory( '.' ) //ftp.changeWorkingDirectory( 'serverFolder' )
+file = new File('README.txt') //new File('localFolder' + File.separator + 'localFilename')
+
+file.withOutputStream{ os ->
+ ftp.retrieveFile( 'README.txt', os ) //ftp.retrieveFile( 'serverFilename', os )
+}
+
+// upload file
+file = new File('otherFile.txt') //new File('localFolder' + File.separator + 'localFilename')
+file.withInputStream{ fis -> ftp.storeFile( 'otherFile.txt', fis ) }
+
+// List the files in the directory
+files = ftp.listFiles()
+println "Number of files in dir: $files.length"
+df = DateFormat.getDateInstance( DateFormat.SHORT )
+files.each{ file ->
+ println "${df.format(file.timestamp.time)}\t $file.name"
+}
+
+// Logout from the FTP Server and disconnect
+ftp.logout()
+ftp.disconnect()
+// =>
+// Connected to localhost. 230 User logged in, proceed.
+// Number of files in dir: 2
+// 18/01/07 otherFile.txt
+// 25/04/06 README.txt
+
+
+// Using AntBuilder; for more details, see:
+// http://ant.apache.org/manual/OptionalTasks/ftp.html
+ant = new AntBuilder()
+ant.ftp(action:'send', server:'ftp.hypothetical.india.org', port:'2121',
+ remotedir:'/pub/incoming', userid:'coder', password:'java1',
+ depends:'yes', binary:'no', systemTypeKey:'Windows',
+ serverTimeZoneConfig:'India/Calcutta'){
+ fileset(dir:'htdocs/manual'){
+ include(name:'**/*.html')
+ }
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_18.3
+//----------------------------------------------------------------------------------
+// using AntBuilder; for more info, see:
+// http://ant.apache.org/manual/CoreTasks/mail.html
+ant = new AntBuilder()
+ant.mail(mailhost:'smtp.myisp.com', mailport:'1025', subject:'Test build'){
+ from(address:'config@myisp.com')
+ replyto(address:'me@myisp.com')
+ to(address:'all@xyz.com')
+ message("The ${buildname} nightly build has completed")
+ attachments(){ // ant 1.7 uses files attribute in earlier versions
+ fileset(dir:'dist'){
+ include(name:'**/*.zip')
+ }
+ }
+}
+
+// using commons net
+import org.apache.commons.net.smtp.*
+client = new SMTPClient()
+client.connect( "mail.myserver.com", 25 )
+if( !SMTPReply.isPositiveCompletion(client.replyCode) ) {
+ client.disconnect()
+ System.err.println("SMTP server refused connection.")
+ System.exit(1)
+}
+
+// Login
+client.login( "myserver.com" )
+
+// Set the sender and recipient(s)
+client.setSender( "config@myisp.com" )
+client.addRecipient( "all@xyz.com" )
+
+// Use the SimpleSMTPHeader class to build the header
+writer = new PrintWriter( client.sendMessageData() )
+header = new SimpleSMTPHeader( "config@myisp.com", "all@xyz.com", "My Subject")
+header.addCC( "me@myisp.com" )
+header.addHeaderField( "Organization", "My Company" )
+
+// Write the header to the SMTP Server
+writer.write( header.toString() )
+
+// Write the body of the message
+writer.write( "This is a test..." )
+
+// Close the writer
+writer.close()
+if ( !client.completePendingCommand() ) // failure
+ System.exit( 1 )
+
+// Logout from the e-mail server (QUIT) and close connection
+client.logout()
+client.disconnect()
+
+// You can also use JavaMail; for more details, see:
+// http://java.sun.com/products/javamail/
+
+// For testing programs which send emails, consider:
+// Dumbster (http://quintanasoft.com/dumbster/)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_18.4
+//----------------------------------------------------------------------------------
+// slight variation to original cookbook:
+// prints 1st, 2nd and last articles from random newsgroup
+import org.apache.commons.net.nntp.NNTPClient
+postingPerm = ['Unknown', 'Moderated', 'Permitted', 'Prohibited']
+client = new NNTPClient()
+client.connect("news.example.com")
+list = client.listNewsgroups()
+println "Found ${list.size()} newsgroups"
+aList = list[new Random().nextInt(list.size())]
+println "$aList.newsgroup has $aList.articleCount articles"
+println "PostingPermission = ${postingPerm[aList.postingPermission]}"
+first = aList.firstArticle
+println "First=$first, Last=$aList.lastArticle"
+client.retrieveArticle(first)?.eachLine{ println it }
+client.selectNextArticle()
+client.retrieveArticle()?.eachLine{ println it }
+client.retrieveArticle(aList.lastArticle)?.eachLine{ println it }
+writer = client.postArticle()
+// ... use writer ...
+writer.close()
+client.logout()
+if (client.isConnected()) client.disconnect()
+// =>
+// Found 37025 newsgroups
+// alt.comp.sys.palmtops.pilot has 730 articles
+// PostingPermission = Permitted
+// First=21904, Last=22633
+// ...
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_18.5
+//----------------------------------------------------------------------------------
+// slight variation to original cookbook to print summary of messages on server
+// uses commons net
+import org.apache.commons.net.pop3.POP3Client
+server = 'pop.myisp.com'
+username = 'gnat'
+password = 'S33kr1T Pa55w0rD'
+timeoutMillis = 30000
+
+def printMessageInfo(reader, id) {
+ def from, subject
+ reader.eachLine{ line ->
+ lower = line.toLowerCase()
+ if (lower.startsWith("from: ")) from = line[6..-1].trim()
+ else if (lower.startsWith("subject: ")) subject = line[9..-1].trim()
+ }
+ println "$id From: $from, Subject: $subject"
+}
+
+pop3 = new POP3Client()
+pop3.setDefaultTimeout(timeoutMillis)
+pop3.connect(server)
+
+if (!pop3.login(username, password)) {
+ System.err.println("Could not login to server. Check password.")
+ pop3.disconnect()
+ System.exit(1)
+}
+messages = pop3.listMessages()
+if (!messages) System.err.println("Could not retrieve message list.")
+else if (messages.length == 0) println("No messages")
+else {
+ messages.each{ message ->
+ reader = pop3.retrieveMessageTop(message.number, 0)
+ if (!reader) {
+ System.err.println("Could not retrieve message header. Skipping...")
+ }
+ printMessageInfo(new BufferedReader(reader), message.number)
+ }
+}
+
+pop3.logout()
+pop3.disconnect()
+
+// You can also use JavaMail; for more details, see:
+// http://java.sun.com/products/javamail/
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_18.6
+//----------------------------------------------------------------------------------
+// Variation to original cookbook: this more extensive example
+// uses telnet to extract weather information about Sydney from
+// a telnet-based weather server at the University of Michigan.
+import org.apache.commons.net.telnet.TelnetClient
+
+def readUntil( pattern ) {
+ sb = new StringBuffer()
+ while ((ch = reader.read()) != -1) {
+ sb << (char) ch
+ if (sb.toString().endsWith(pattern)) {
+ def found = sb.toString()
+ sb = new StringBuffer()
+ return found
+ }
+ }
+ return null
+}
+
+telnet = new TelnetClient()
+telnet.connect( 'rainmaker.wunderground.com', 3000 )
+reader = telnet.inputStream.newReader()
+writer = new PrintWriter(new OutputStreamWriter(telnet.outputStream),true)
+readUntil( "Welcome" )
+println 'Welcome' + readUntil( "!" )
+readUntil( "continue:" )
+writer.println()
+readUntil( "-- " )
+writer.println()
+readUntil( "Selection:" )
+writer.println("10")
+readUntil( "Selection:" )
+writer.println("3")
+x = readUntil( "Return" )
+while (!x.contains('SYDNEY')) {
+ writer.println()
+ x = readUntil( "Return" )
+}
+m = (x =~ /(?sm).*(SYDNEY.*?)$/)
+telnet.disconnect()
+println m[0][1]
+// =>
+// Welcome to THE WEATHER UNDERGROUND telnet service!
+// SYDNEY FAIR 10AM 81 27
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_18.7
+//----------------------------------------------------------------------------------
+address = InetAddress.getByName("web.mit.edu")
+timeoutMillis = 3000
+println address.isReachable(timeoutMillis)
+// => true (if firewalls don't get in the way, may require privileges on Linux,
+// may not use ICMP but rather Echo protocol on Windows machines)
+
+// You can also use commons net EchoUDPClient and EchoTCPClient to interact
+// with the Echo protocol - sometimes useful for ping-like functionality.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_18.8
+//----------------------------------------------------------------------------------
+import org.apache.commons.net.WhoisClient
+whois = new WhoisClient()
+whois.connect(WhoisClient.DEFAULT_HOST)
+result = whois.query('cnn.com') // as text of complete query
+println result // could extract info from result here (using e.g. regex)
+whois.disconnect()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_18.9
+//----------------------------------------------------------------------------------
+// not exact equivalent to original cookbook: just shows raw functionality
+client = new SMTPClient()
+client.connect( "smtp.example.com", 25 )
+println client.verify("george") // => true
+println client.replyString // => 250 George Washington <george@wash.dc.gov>
+println client.verify("jetson") // => false
+println client.replyString // => 550 jetson... User unknown
+client.expn("presidents")
+println client.replyString
+// =>
+// 250-George Washington <george@wash.dc.gov>
+// 250-Thomas Jefferson <tj@wash.dc.gov>
+// 250-Ben Franklin <ben@here.us.edu>
+// ...
+
+// expect these commands to be disabled by most public servers due to spam
+println client.replyString
+// => 502 Command is locally disabled
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.0
+//----------------------------------------------------------------------------------
+// URLs have the same form as in Perl
+
+// Invoking dynamic content is done through the same standard urls:
+// http://mox.perl.com/cgi-bin/program?name=Johann&born=1685
+// http://mox.perl.com/cgi-bin/program
+
+// Groovy has Groovelets and GSP page support built-in. For a full
+// web framework, see Grails: http://grails.codehaus.org/
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.1
+//----------------------------------------------------------------------------------
+// as a plain groovelet
+param = request.getParameter('PARAM_NAME')
+println """
+<html><head>
+<title>Howdy there!</title>
+</head>
+<body>
+<p>
+You typed: $param
+</p>
+</body>
+</html>
+"""
+
+// as a groovelet using markup builder
+import groovy.xml.MarkupBuilder
+writer = new StringWriter()
+builder = new MarkupBuilder(writer)
+builder.html {
+ head {
+ title 'Howdy there!'
+ }
+ body {
+ p('You typed: ' + request.getParameter('PARAM_NAME'))
+ }
+}
+println writer.toString()
+
+// as a GSP page:
+<html><head>
+<title>Howdy there!</title>
+</head>
+<body>
+<p>
+You typed: ${request.getParameter('PARAM_NAME')}
+</p>
+</body>
+</html>
+
+// Request parameters are often encoded by the browser before
+// sending to the server and usually can be printed out as is.
+// If you need to convert, use commons lang StringEscapeUtils#escapeHtml()
+// and StringEscapeUtils#unescapeHtml().
+
+// Getting parameters:
+who = request.getParameter('Name')
+phone = request.getParameter('Number')
+picks = request.getParameterValues('Choices') // String array or null
+
+// Changing headers:
+response.setContentType('text/html;charset=UTF-8')
+response.setContentType('text/plain')
+response.setContentType('text/plain')
+response.setHeader('Cache-control', 'no-cache')
+response.setDateHeader('Expires', System.currentTimeMillis() + 3*24*60*60*1000)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.2
+//----------------------------------------------------------------------------------
+// The Java Servlet API has a special log() method for writing to the
+// web server log.
+
+// To send errors to custom HTML pages, update the web.xml deployment
+// descriptor to include one or more <error-page> elements, e.g.:
+<error-page>
+ <error-code>404</error-code>
+ <location>/404.html</location>
+</error-page>
+<error-page>
+ <exception-type>java.lang.NullPointerException</exception-type>
+ <location>/NpeError.gsp</location>
+</error-page>
+
+// Another trick is to catch an exception within the servlet/gsp code
+// and print it out into the HTML as a comment.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.3
+//----------------------------------------------------------------------------------
+// 500 errors could occur if you have compile errors in your script.
+// Pre-compile with your IDE or groovyc.
+
+// You can use an expando, mock or map to run your scripts outside
+// the web container environment. If you use Jetty as your container
+// it has a special servlet tester, for more details:
+// http://blogs.webtide.com/gregw/2006/12/16/1166307599250.html
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.4
+//----------------------------------------------------------------------------------
+// Web servers should be invoked with an appropriate Java security policy in place.
+// This can be used to limit possible actions from hacking attempts.
+
+// Normal practices limit hacking exposure. The JDBC API encourages the use
+// of Prepared queries rather than encouraging practices which lead to SQL
+// injection. Using system or exec is rarely used either as Java provides
+// cross-platform mechanisms for most operating system level functionality.
+
+// Other security measures should be complemented with SSL and authentication.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.5
+//----------------------------------------------------------------------------------
+// Within the servlet element of your web.xml, there is a <load-on-startup> element.
+// Use that on a per servlet basis to pre-load whichever servlets you like.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.6
+//----------------------------------------------------------------------------------
+// As discussed in 19.3 and 19.4:
+// Web servers should be invoked with an appropriate Java security policy in place.
+// This can be used to limit possible actions from hacking attempts.
+
+// Normal practices limit hacking exposure. The JDBC API encourages the use
+// of Prepared queries rather than encouraging practices which lead to SQL
+// injection. Using system or exec is rarely used either as Java provides
+// cross-platform mechanisms for most operating system level functionality.
+
+// In addition, if authentication is used, security can be locked down at a
+// very fine-grained level on a per servlet action or per user (with JAAS) basis.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.7
+//----------------------------------------------------------------------------------
+import groovy.xml.*
+// using a builder:
+Closure markup = {
+ ol {
+ ['red','blue','green'].each{ li(it) }
+ }
+}
+println new StreamingMarkupBuilder().bind(markup).toString()
+// => <ol><li>red</li><li>blue</li><li>green</li></ol>
+
+names = 'Larry Moe Curly'.split(' ')
+markup = {
+ ul {
+ names.each{ li(type:'disc', it) }
+ }
+}
+println new StreamingMarkupBuilder().bind(markup).toString()
+// <ul><li type="disc">Larry</li><li type="disc">Moe</li>
+// <li type="disc">Curly</li></ul>
+//-----------------------------
+
+m = { li("alpha") }
+println new StreamingMarkupBuilder().bind(m).toString()
+// <li>alpha</li>
+
+m = { ['alpha','omega'].each { li(it) } }
+println new StreamingMarkupBuilder().bind(m).toString()
+// <li>alpha</li> <li>omega</li>
+//-----------------------------
+
+states = [
+ "Wisconsin": [ "Superior", "Lake Geneva", "Madison" ],
+ "Colorado": [ "Denver", "Fort Collins", "Boulder" ],
+ "Texas": [ "Plano", "Austin", "Fort Stockton" ],
+ "California": [ "Sebastopol", "Santa Rosa", "Berkeley" ],
+]
+
+writer = new StringWriter()
+builder = new MarkupBuilder(writer)
+builder.table{
+ caption('Cities I Have Known')
+ tr{ th('State'); th(colspan:3, 'Cities') }
+ states.keySet().sort().each{ state ->
+ tr{
+ th(state)
+ states[state].sort().each{ td(it) }
+ }
+ }
+}
+println writer.toString()
+// =>
+// <table>
+// <caption>Cities I Have Known</caption>
+// <tr>
+// <th>State</th>
+// <th colspan='3'>Cities</th>
+// </tr>
+// <tr>
+// <th>California</th>
+// <td>Berkeley</td>
+// <td>Santa Rosa</td>
+// <td>Sebastopol</td>
+// </tr>
+// <tr>
+// <th>Colorado</th>
+// <td>Boulder</td>
+// <td>Denver</td>
+// <td>Fort Collins</td>
+// </tr>
+// <tr>
+// <th>Texas</th>
+// <td>Austin</td>
+// <td>Fort Stockton</td>
+// <td>Plano</td>
+// </tr>
+// <tr>
+// <th>Wisconsin</th>
+// <td>Lake Geneva</td>
+// <td>Madison</td>
+// <td>Superior</td>
+// </tr>
+// </table>
+
+import groovy.sql.Sql
+import groovy.xml.MarkupBuilder
+
+dbHandle = null
+dbUrl = 'jdbc:hsqldb:...'
+def getDb(){
+ if (dbHandle) return dbHandle
+ def source = new org.hsqldb.jdbc.jdbcDataSource()
+ source.database = dbUrl
+ source.user = 'sa'
+ source.password = ''
+ dbHandle = new Sql(source)
+ return dbHandle
+}
+
+def findByLimit(limit) {
+ db.rows "SELECT name,salary FROM employees where salary > $limit"
+}
+
+limit = request.getParameter('LIMIT')
+writer = new StringWriter()
+builder = new MarkupBuilder(writer)
+builder.html {
+ head { title('Salary Query') }
+ h1('Search')
+ form{
+ p('Enter minimum salary')
+ input(type:'text', name:'LIMIT')
+ input(type:'submit')
+ }
+ if (limit) {
+ h1('Results')
+ table(border:1){
+ findByLimit(limit).each{ row ->
+ tr{ td(row.name); td(row.salary) }
+ }
+ }
+ }
+}
+println writer.toString()
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.8
+//----------------------------------------------------------------------------------
+// The preferred way to redirect to resources within the web application:
+dispatcher = request.getRequestDispatcher('hello.gsp')
+dispatcher.forward(request, response)
+// Old versions of web containers allowed this mechanism to also redirect
+// to external resources but this was deemed a potential security risk.
+
+// The suggested way to external sites (less efficient for internal resources):
+response.sendRedirect("http://www.perl.com/CPAN/")
+
+// set cookie and forward
+oreo = new Cookie('filling', 'vanilla creme')
+THREE_MONTHS = 3 * 30 * 24 * 60 * 60
+oreo.maxAge = THREE_MONTHS
+oreo.domain = '.pleac.sourceforge.net'
+whither = 'http://pleac.sourceforge.net/pleac_ruby/cgiprogramming.html'
+response.addCookie(oreo)
+response.sendRedirect(whither)
+
+// forward based on user agent
+dir = 'http://www.science.uva.nl/%7Emes/jargon'
+agent = request.getHeader('user-agent')
+menu = [
+ [/Mac/, 'm/macintrash.html'],
+ [/Win(dows )?NT/, 'e/evilandrude.html'],
+ [/Win|MSIE|WebTV/, 'm/microslothwindows.html'],
+ [/Linux/, 'l/linux.html'],
+ [/HP-UX/, 'h/hpsux.html'],
+ [/SunOS/, 's/scumos.html'],
+]
+page = 'a/aportraitofj.randomhacker.html'
+menu.each{
+ if (agent =~ it[0]) page = it[1]
+}
+response.sendRedirect("$dir/$page")
+
+// no response output
+response.sendError(204, 'No Response')
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.9
+//----------------------------------------------------------------------------------
+// Consider TCPMON or similar: http://ws.apache.org/commons/tcpmon/
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.10
+//----------------------------------------------------------------------------------
+// helper method
+import javax.servlet.http.Cookie
+import groovy.xml.MarkupBuilder
+
+def getCookieValue(cookies, cookieName, defaultValue) {
+ if (cookies) for (i in 0..<cookies.length) {
+ if (cookieName == cookies[i].name) return cookies[i].value
+ }
+ return defaultValue
+}
+
+prefValue = getCookieValue(request.cookies, 'preference_name', 'default')
+cookie = new Cookie('preference name',"whatever you'd like")
+SECONDS_PER_YEAR = 60*60*24*365
+cookie.maxAge = SECONDS_PER_YEAR * 2
+response.addCookie(cookie)
+
+cookname = 'fav_ice_cream'
+favorite = request.getParameter('flavor')
+tasty = getCookieValue(request.cookies, cookname, 'mint')
+
+writer = new StringWriter()
+builder = new MarkupBuilder(writer)
+builder.html {
+ head { title('Ice Cookies') }
+ body {
+ h1('Hello Ice Cream')
+ if (favorite) {
+ p("You chose as your favorite flavor '$favorite'.")
+ cookie = new Cookie(cookname, favorite)
+ ONE_HOUR = 3600 // secs
+ cookie.maxAge = ONE_HOUR
+ response.addCookie(cookie)
+ } else {
+ hr()
+ form {
+ p('Please select a flavor: ')
+ input(type:'text', name:'flavor', value:tasty)
+ }
+ hr()
+ }
+ }
+}
+println writer.toString()
+//----------------------------------------------------------------------------------
+
+// @@PLEAC@@_19.11
+//----------------------------------------------------------------------------------
+import groovy.xml.MarkupBuilder
+// On Linux systems replace with: "who".execute().text
+fakedWhoInput = '''
+root tty1 Nov 2 17:57
+hermie tty3 Nov 2 18:43
+hermie tty4 Nov 1 20:01
+sigmund tty2 Nov 2 18:08
+'''.trim().split(/\n/)
+name = request.getParameter('WHO')
+if (!name) name = ''
+writer = new StringWriter()
+new MarkupBuilder(writer).html{
+ head{ title('Query Users') }
+ body{
+ h1('Search')
+ form{
+ p('Which User?')
+ input(type:'text', name:'WHO', value:name)
+ input(type:'submit')
+ }
+ if (name) {
+ h1('Results')
+ lines = fakedWhoInput.grep(~/^$name\s.*/)
+ if (lines) message = lines.join('\n')
+ else message = "$name is not logged in"
+ pre(message)
+ }
+ }
+}
+println writer.toString()
+// if you need to escape special symbols, e.g. '<' or '>' use commons lang StringEscapeUtils
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.12
+//----------------------------------------------------------------------------------
+// frameworks typically do this for you, but shown here are the manual steps
+// even when doing it manually, you would probably use session variables
+
+// setting a hidden field
+input(type:'hidden', value:'bacon')
+
+// setting a value on the submit
+input(type:'submit', name:".State", value:'Checkout')
+
+// determining 'mode'
+page = request.getParameter('.State')
+if (!page) page = 'Default'
+
+// forking with if chain
+if (page == "Default") {
+ frontPage()
+} else if (page == "Checkout") {
+ checkout()
+} else {
+ noSuchPage()
+}
+
+// forking with map
+states = [
+ Default: this.&frontPage,
+ Shirt: this.&tShirt,
+ Sweater: this.&sweater,
+ Checkout: this.&checkout,
+ Card: this.&creditCard,
+ Order: this.&order,
+ Cancel: this.&frontPage,
+]
+
+// calling each to allow hidden variable saving
+states.each{ key, closure ->
+ closure(page == key)
+}
+
+// exemplar method
+def tShirt(active) {
+ def sizes = ['XL', 'L', 'M', 'S']
+ def colors = ['Black', 'White']
+ if (!active) {
+ hidden("size")
+ hidden("color")
+ return
+ }
+ p("You want to buy a t-shirt?");
+ label("Size: "); dropDown("size", sizes)
+ label("Color: "); dropDown("color", colors)
+ shopMenu()
+}
+
+// kicking off processing
+html{
+ head{ title('chemiserie store') }
+ body {
+ if (states[page]) process(page)
+ else noSuchPage()
+ }
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.13
+//----------------------------------------------------------------------------------
+// get request parameters as map
+map = request.parameterMap
+
+// save to file
+new File(filename).withOutputStream{ fos ->
+ oos = new ObjectOutputStream(fos)
+ oos.writeObject(map)
+ oos.close()
+}
+
+// convert to text
+sb = new StringBuffer()
+map.each{ k,v -> sb << "$k=$v" }
+text = sb.toString()
+// to send text via email, see 18.3
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_19.14
+//----------------------------------------------------------------------------------
+// you wouldn't normally do it this way, consider a framework like Grails
+// even when doing it by hand, you would probably use session variables
+import groovy.xml.MarkupBuilder
+
+page = param('.State', 'Default')
+
+states = [
+ Default: this.&frontPage,
+ Shirt: this.&shirt,
+ Sweater: this.&sweater,
+ Checkout: this.&checkout,
+ Card: this.&creditCard,
+ Order: this.&order,
+ Cancel: this.&frontPage,
+]
+
+writer = new StringWriter()
+b = new MarkupBuilder(writer)
+b.html{
+ head{ title('chemiserie store') }
+ body {
+ if (states[page]) process(page)
+ else noSuchPage()
+ }
+}
+println writer.toString()
+
+def process(page) {
+ b.form{
+ states.each{ key, closure ->
+ closure(page == key)
+ }
+ }
+}
+
+def noSuchPage() {
+ b.p('Unknown request')
+ reset('Click here to start over')
+}
+
+def shopMenu() {
+ b.p()
+ toPage("Shirt")
+ toPage("Sweater")
+ toPage("Checkout")
+ reset('Empty My Shopping Cart')
+}
+
+def frontPage(active) {
+ if (!active) return
+ b.h1('Hi!')
+ b.p('Welcome to our Shirt Shop! Please make your selection from the menu below.')
+ shopMenu()
+}
+
+def shirt(active) {
+ def sizes = ['XL', 'L', 'M', 'S']
+ def colors = ['Black', 'White']
+ def count = param('shirt_count',0)
+ def color = param('shirt_color')
+ def size = param('shirt_size')
+ // sanity check
+ if (count) {
+ if (!(color in colors)) color = colors[0]
+ if (!(size in sizes)) size = sizes[0]
+ }
+ if (!active) {
+ if (size) hidden("shirt_size", size)
+ if (color) hidden("shirt_color", color)
+ if (count) hidden("shirt_count", count)
+ return
+ }
+ b.h1 'T-Shirt'
+ b.p '''What a shirt! This baby is decked out with all the options.
+ It comes with full luxury interior, cotton trim, and a collar
+ to make your eyes water! Unit price: $33.00'''
+ b.h2 'Options'
+ label("How Many?"); textfield("shirt_count")
+ label("Size?"); dropDown("shirt_size", sizes)
+ label("Color?"); dropDown("shirt_color", colors)
+ shopMenu()
+}
+
+def sweater(active) {
+ def sizes = ['XL', 'L', 'M']
+ def colors = ['Chartreuse', 'Puce', 'Lavender']
+ def count = param('sweater_count',0)
+ def color = param('sweater_color')
+ def size = param('sweater_size')
+ // sanity check
+ if (count) {
+ if (!(color in colors)) color = colors[0]
+ if (!(size in sizes)) size = sizes[0]
+ }
+ if (!active) {
+ if (size) hidden("sweater_size", size)
+ if (color) hidden("sweater_color", color)
+ if (count) hidden("sweater_count", count)
+ return
+ }
+ b.h1("Sweater")
+ b.p("Nothing implies preppy elegance more than this fine " +
+ "sweater. Made by peasant workers from black market silk, " +
+ "it slides onto your lean form and cries out ``Take me, " +
+ "for I am a god!''. Unit price: \$49.99.")
+ b.h2("Options")
+ label("How Many?"); textfield("sweater_count")
+ label("Size?"); dropDown("sweater_size", sizes)
+ label("Color?"); dropDown("sweater_color", colors)
+ shopMenu()
+}
+
+def checkout(active) {
+ if (!active) return
+ b.h1("Order Confirmation")
+ b.p("You ordered the following:")
+ orderText()
+ b.p("Is this right? Select 'Card' to pay for the items" +
+ "or 'Shirt' or 'Sweater' to continue shopping.")
+ toPage("Card")
+ toPage("Shirt")
+ toPage("Sweater")
+}
+
+def creditCard(active) {
+ def widgets = 'Name Address1 Address2 City Zip State Phone Card Expiry'.split(' ')
+ if (!active) {
+ widgets.each{ hidden(it) }
+ return
+ }
+ b.pre{
+ label("Name: "); textfield("Name")
+ label("Address: "); textfield("Address1")
+ label(" "); textfield("Address2")
+ label("City: "); textfield("City")
+ label("Zip: "); textfield("Zip")
+ label("State: "); textfield("State")
+ label("Phone: "); textfield("Phone")
+ label("Credit Card #: "); textfield("Card")
+ label("Expiry: "); textfield("Expiry")
+ }
+ b.p("Click on 'Order' to order the items. Click on 'Cancel' to return shopping.")
+ toPage("Order")
+ toPage("Cancel")
+}
+
+def order(active) {
+ if (!active) return
+ b.h1("Ordered!")
+ b.p("You have ordered the following items:")
+ orderText()
+ reset('Begin Again')
+}
+
+def orderText() {
+ def shirts = param('shirt_count')
+ def sweaters = param('sweater_count')
+ if (shirts) {
+ b.p("""You have ordered ${param('shirt_count')}
+ shirts of size ${param('shirt_size')}
+ and color ${param("shirt_color")}.""")
+ }
+ if (sweaters) {
+ b.p("""You have ordered ${param('sweater_count')}
+ sweaters of size ${param('sweater_size')}
+ and color ${param('sweater_color')}.""")
+ }
+ if (!sweaters && !shirts) b.p("Nothing!")
+ b.p("For a total cost of ${calcPrice()}")
+}
+
+def label(text) { b.span(text) }
+def reset(text) { b.a(href:request.requestURI,text) }
+def toPage(name) { b.input(type:'submit', name:'.State', value:name) }
+def dropDown(name, values) {
+ b.select(name:name){
+ values.each{
+ if (param(name)==it) option(value:it, selected:true, it)
+ else option(value:it, it)
+ }
+ }
+ b.br()
+}
+def hidden(name) {
+ if (binding.variables.containsKey(name)) v = binding[name]
+ else v = ''
+ hidden(name, v)
+}
+def hidden(name, value) { b.input(type:'hidden', name:name, value:value) }
+def textfield(name) { b.input(type:'text', name:name, value:param(name,'')); b.br() }
+def param(name) { request.getParameter(name) }
+def param(name, defValue) {
+ def val = request.getParameter(name)
+ if (val) return val else return defValue
+}
+
+def calcPrice() {
+ def shirts = param('shirt_count', 0).toInteger()
+ def sweaters = param('sweater_count', 0).toInteger()
+ return (shirts * 33 + sweaters * 49.99).toString()
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.0
+//----------------------------------------------------------------------------------
+// Many packages are available for simulating a browser. A good starting point:
+// http://groovy.codehaus.org/Testing+Web+Applications
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.1
+//----------------------------------------------------------------------------------
+// for non-binary content
+urlStr = 'http://groovy.codehaus.org'
+content = new URL(urlStr).text
+println content.size() // => 34824
+
+// for binary content
+urlStr = 'http://groovy.codehaus.org/download/attachments/1871/gina_3d.gif'
+bytes = new ByteArrayOutputStream()
+bytes << new URL(urlStr).openStream()
+println bytes.size() // => 6066
+
+// various forms of potential error checking
+try {
+ new URL('x:y:z')
+} catch (MalformedURLException ex) {
+ println ex.message // => unknown protocol: x
+}
+try {
+ new URL('cnn.com/not.there')
+} catch (MalformedURLException ex) {
+ println ex.message // => no protocol: cnn.com/not.there
+}
+try {
+ content = new URL('http://cnn.com/not.there').text
+} catch (FileNotFoundException ex) {
+ println "Couldn't find: " + ex.message
+ // => Couldn't find: http://www.cnn.com/not.there
+}
+
+// titleBytes example
+def titleBytes(urlStr) {
+ def lineCount = 0; def byteCount = 0
+ new URL(urlStr).eachLine{ line ->
+ lineCount++; byteCount += line.size()
+ }
+ println "$urlStr => ($lineCount lines, $byteCount bytes)"
+}
+titleBytes('http://www.tpj.com/')
+// http://www.tpj.com/ => (677 lines, 25503 bytes)
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.2
+//----------------------------------------------------------------------------------
+// using HtmlUnit (htmlunit.sf.net)
+import com.gargoylesoftware.htmlunit.WebClient
+
+def webClient = new WebClient()
+def page = webClient.getPage('http://search.cpan.org/')
+// check page title
+assert page.titleText.startsWith('The CPAN Search Site')
+// fill in form and submit it
+def form = page.getFormByName('f')
+def field = form.getInputByName('query')
+field.setValueAttribute('DB_File')
+def button = form.getInputByValue('CPAN Search')
+def result = button.click()
+// check search result has at least one link ending in DB_File.pm
+assert result.anchors.any{ a -> a.hrefAttribute.endsWith('DB_File.pm') }
+
+// fields must be properly escaped
+println URLEncoder.encode(/"this isn't <EASY>&<FUN>"/, 'utf-8')
+// => %22this+isn%27t+%3CEASY%3E%26%3CFUN%3E%22
+
+// proxies can be taken from environment, or specified
+//System.properties.putAll( ["http.proxyHost":"proxy-host", "http.proxyPort":"proxy-port",
+// "http.proxyUserName":"user-name", "http.proxyPassword":"proxy-passwd"] )
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.3
+//----------------------------------------------------------------------------------
+// using HtmlUnit (htmlunit.sf.net)
+import com.gargoylesoftware.htmlunit.WebClient
+
+client = new WebClient()
+html = client.getPage('http://www.perl.com/CPAN/')
+println page.anchors.collect{ it.hrefAttribute }.sort().unique().join('\n')
+// =>
+// disclaimer.html
+// http://bookmarks.cpan.org/
+// http://faq.perl.org/
+// mailto:cpan@perl.org
+// ...
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.4
+//----------------------------------------------------------------------------------
+// split paragraphs
+LS = System.properties.'line.separator'
+new File(args[0]).text.split("$LS$LS").each{ para ->
+ if (para.startsWith(" ")) println "<pre>\n$para\n</pre>"
+ else {
+ para = para.replaceAll(/(?m)^(>.*?)$/, /$1<br \/>/) // quoted text
+ para = para.replaceAll(/<URL:(.*)>/, /<a href="$1">$1<\/a>/) // embedded URL
+ para = para.replaceAll(/(http:\S+)/, /<a href="$1">$1<\/a>/) // guessed URL
+ para = para.replaceAll('\\*(\\S+)\\*', /<strong>$1<\/strong>/) // this is *bold* here
+ para = para.replaceAll(/\b_(\S+)_\b/, /<em>$1<\/em>/) // this is _italic_ here
+ println "<p>\n$para\n</p>" // add paragraph tags
+ }
+}
+
+def encodeEmail(email) {
+ println "<table>"
+ email = URLEncoder.encode(email)
+ email = text.replaceAll(/(\n[ \t]+)/, / . /) // continuation lines
+ email = text.replaceAll(/(?m)^(\S+?:)\s*(.*?)$/,
+ /<tr><th align="left">$1<\/th><td>$2<\/td><\/tr>/);
+ println email
+ println "</table>"
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.5
+//----------------------------------------------------------------------------------
+// using CyberNeko Parser (people.apache.org/~andyc/neko/doc)
+parser = new org.cyberneko.html.parsers.SAXParser()
+parser.setFeature('http://xml.org/sax/features/namespaces', false)
+page = new XmlParser(parser).parse('http://www.perl.com/CPAN/')
+page.depthFirst().each{ println it.text() }
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.6
+//----------------------------------------------------------------------------------
+// removing tags, see 20.5
+
+// extracting tags: htitle using cyberneko and XmlSlurper
+parser = new org.cyberneko.html.parsers.SAXParser()
+parser.setFeature('http://xml.org/sax/features/namespaces', false)
+page = new XmlParser(parser).parse('http://www.perl.com/CPAN/')
+println page.HEAD.TITLE[0].text()
+
+// extracting tags: htitle using HtmlUnit
+client = new WebClient()
+html = client.getPage('http://www.perl.com/CPAN/')
+println html.titleText
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.7
+//----------------------------------------------------------------------------------
+import com.gargoylesoftware.htmlunit.WebClient
+
+client = new WebClient()
+page = client.getPage('http://www.perl.com/CPAN/')
+page.anchors.each{
+ checkUrl(page, it.hrefAttribute)
+}
+
+def checkUrl(page, url) {
+ try {
+ print "$url "
+ qurl = page.getFullyQualifiedUrl(url)
+ client.getPage(qurl)
+ println 'OK'
+ } catch (Exception ex) {
+ println 'BAD'
+ }
+}
+// =>
+// modules/index.html OK
+// RECENT.html OK
+// http://search.cpan.org/recent OK
+// http://mirrors.cpan.org/ OK
+// http://perldoc.perl.org/ OK
+// mailto:cpan@perl.org BAD
+// http://www.csc.fi/suomi/funet/verkko.html.en/ BAD
+// ...
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.8
+//----------------------------------------------------------------------------------
+import org.apache.commons.httpclient.HttpClient
+import org.apache.commons.httpclient.methods.HeadMethod
+import java.text.DateFormat
+
+urls = [
+ "http://www.apache.org/",
+ "http://www.perl.org/",
+ "http://www.python.org/",
+ "http://www.ora.com/",
+ "http://jakarta.apache.org/",
+ "http://www.w3.org/"
+]
+
+df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.MEDIUM)
+client = new HttpClient()
+urlInfo = [:]
+urls.each{ url ->
+ head = new HeadMethod(url)
+ client.executeMethod(head)
+ lastModified = head.getResponseHeader("last-modified")?.value
+ urlInfo[df.parse(lastModified)]=url
+}
+
+urlInfo.keySet().sort().each{ key ->
+ println "$key ${urlInfo[key]}"
+}
+// =>
+// Sun Jan 07 21:48:15 EST 2007 http://www.apache.org/
+// Sat Jan 13 12:44:32 EST 2007 http://jakarta.apache.org/
+// Fri Jan 19 14:50:13 EST 2007 http://www.w3.org/
+// Fri Jan 19 19:28:35 EST 2007 http://www.python.org/
+// Sat Jan 20 09:36:08 EST 2007 http://www.ora.com/
+// Sat Jan 20 13:25:53 EST 2007 http://www.perl.org/
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.9
+//----------------------------------------------------------------------------------
+// GString version (variables must be predefined):
+username = 'Tom'
+count = 99
+total = 999
+htmlStr = """
+<!-- simple.template for internal template() function -->
+<HTML><HEAD><TITLE>Report for $username</TITLE></HEAD>
+<BODY><H1>Report for $username</H1>
+$username logged in $count times, for a total of $total minutes.
+"""
+println htmlStr
+
+// SimpleTemplateEngine version:
+def html = '''
+<!-- simple.template for internal template() function -->
+<HTML><HEAD><TITLE>Report for $username</TITLE></HEAD>
+<BODY><H1>Report for $username</H1>
+$username logged in $count times, for a total of $total minutes.
+'''
+
+def engine = new groovy.text.SimpleTemplateEngine()
+def reader = new StringReader(html)
+def template = engine.createTemplate(reader)
+println template.make(username:"Peter", count:"23", total: "1234")
+
+// SQL version
+import groovy.sql.Sql
+user = 'Peter'
+def sql = Sql.newInstance('jdbc:mysql://localhost:3306/mydb', 'dbuser',
+ 'dbpass', 'com.mysql.jdbc.Driver')
+sql.query("SELECT COUNT(duration),SUM(duration) FROM logins WHERE username='$user'") { answer ->
+ println (template.make(username:user, count:answer[0], total:answer[1]))
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.10
+//----------------------------------------------------------------------------------
+// using built-in connection features
+urlStr = 'http://jakarta.apache.org/'
+url = new URL(urlStr)
+connection = url.openConnection()
+connection.ifModifiedSince = new Date(2007,1,18).time
+connection.connect()
+println connection.responseCode
+
+// manually setting header field
+connection = url.openConnection()
+df = new java.text.SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss 'GMT'")
+df.setTimeZone(TimeZone.getTimeZone('GMT'))
+connection.setRequestProperty("If-Modified-Since",df.format(new Date(2007,1,18)));
+connection.connect()
+println connection.responseCode
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.11
+//----------------------------------------------------------------------------------
+// The website http://www.robotstxt.org/wc/active/html/ lists many available robots
+// including Java ones which can be used from Groovy. In particular, j-spider
+// allows you to:
+// + Check your site for errors (internal server errors, ...)
+// + Outgoing and/or internal link checking
+// + Analyze your site structure (creating a sitemap, ...)
+// + Download complete web sites
+// most of its functionality is available by tweaking appropriate configuration
+// files and then running it as a standalone application but you can also write
+// your own java classes.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.12
+//----------------------------------------------------------------------------------
+// sample data, use 'LOGFILE = new File(args[0]).text' or similar
+LOGFILE = '''
+127.0.0.1 - - [04/Sep/2005:20:50:31 +0200] "GET /bus HTTP/1.1" 301 303
+127.0.0.1 - - [04/Sep/2005:20:50:31 +0200] "GET /bus HTTP/1.1" 301 303 "-" "Opera/8.02 (X11; Linux i686; U; en)"
+192.168.0.1 - - [04/Sep/2005:20:50:36 +0200] "GET /bus/libjs/layersmenu-library.js HTTP/1.1" 200 6228
+192.168.0.1 - - [04/Sep/2005:20:50:36 +0200] "GET /bus/libjs/layersmenu-library.js HTTP/1.1" 200 6228 "http://localhost/bus/" "Opera/8.02 (X11; Linux i686; U; en)"
+'''
+
+// similar to perl version:
+fields = ['client','identuser','authuser','date','time','tz','method','url','protocol','status','bytes']
+regex = /^(\S+) (\S+) (\S+) \[([^:]+):(\d+:\d+:\d+) ([^\]]+)\] "(\S+) (.*?) (\S+)" (\S+) (\S+).*$/
+
+LOGFILE.trim().split('\n').each{ line ->
+ m = line =~ regex
+ if (m.matches()) {
+ for (idx in 0..<fields.size()) { println "${fields[idx]}=${m[0][idx+1]}" }
+ println()
+ }
+}
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.13
+//----------------------------------------------------------------------------------
+// sample data, use 'LOGFILE = new File(args[0]).text' or similar
+LOGFILE = '''
+204.31.113.138 - - [03/Jul/1996:06:56:12 -0800] "POST /forms/login.jsp HTTP/1.0" 200 5593
+fcrawler.looksmart.com - - [26/Apr/2000:00:00:12 -0400] "GET /contacts.html HTTP/1.0" 200 4595 "-" "FAST-WebCrawler/2.1-pre2 (ashen@looksmart.net)"
+fcrawler.looksmart.com - - [26/Apr/2000:00:17:19 -0400] "GET /news/news.html HTTP/1.0" 200 16716 "-" "FAST-WebCrawler/2.1-pre2 (ashen@looksmart.net)"
+ppp931.on.bellglobal.com - - [26/Apr/2000:00:16:12 -0400] "GET /download/windows/asctab31.zip HTTP/1.0" 200 1540096 "http://www.htmlgoodies.com/downloads/freeware/webdevelopment/15.html" "Mozilla/4.7 [en]C-SYMPA (Win95; U)"
+123.123.123.123 - - [26/Apr/2000:00:23:48 -0400] "GET /pics/wpaper.gif HTTP/1.0" 200 6248 "http://www.jafsoft.com/asctortf/" "Mozilla/4.05 (Macintosh; I; PPC)"
+123.123.123.123 - - [26/Apr/2000:00:23:47 -0400] "GET /asctortf/ HTTP/1.0" 200 8130 "http://search.netscape.com/Computers/Data_Formats/Document/Text/RTF" "Mozilla/4.05 (Macintosh; I; PPC)"
+123.123.123.123 - - [26/Apr/2000:00:23:48 -0400] "GET /pics/5star2000.gif HTTP/1.0" 200 4005 "http://www.jafsoft.com/asctortf/" "Mozilla/4.05 (Macintosh; I; PPC)"
+123.123.123.123 - - [27/Apr/2000:00:23:50 -0400] "GET /pics/5star.gif HTTP/1.0" 200 1031 "http://www.jafsoft.com/asctortf/" "Mozilla/4.05 (Macintosh; I; PPC)"
+123.123.123.123 - - [27/Apr/2000:00:23:51 -0400] "GET /pics/a2hlogo.jpg HTTP/1.0" 200 4282 "http://www.jafsoft.com/asctortf/" "Mozilla/4.05 (Macintosh; I; PPC)"
+123.123.123.123 - - [27/Apr/2000:00:23:51 -0400] "GET /cgi-bin/newcount?jafsof3&width=4&font=digital&noshow HTTP/1.0" 200 36 "http://www.jafsoft.com/asctortf/" "Mozilla/4.05 (Macintosh; I; PPC)"
+127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
+127.0.0.1 - - [04/Sep/2005:20:50:31 +0200] "GET / HTTP/1.1" 200 1927
+127.0.0.1 - - [04/Sep/2005:20:50:31 +0200] "GET /bus HTTP/1.1" 301 303 "-" "Opera/8.02 (X11; Linux i686; U; en)"
+192.168.0.1 - - [05/Sep/2005:20:50:36 +0200] "GET /bus/libjs/layersmenu-library.js HTTP/1.1" 200 6228
+192.168.0.1 - - [05/Sep/2005:20:50:36 +0200] "GET /bus/libjs/layersmenu-library.js HTTP/1.1" 200 6228 "http://localhost/bus/" "Opera/8.02 (X11; Linux i686; U; en)"
+'''
+
+fields = ['client','identuser','authuser','date','time','tz','method','url','protocol','status','bytes']
+regex = /^(\S+) (\S+) (\S+) \[([^:]+):(\d+:\d+:\d+) ([^\]]+)\] "(\S+) (.*?) (\S+)" (\S+) (\S+).*$/
+
+class Summary {
+ def hosts = [:]
+ def what = [:]
+ def accessCount = 0
+ def postCount = 0
+ def homeCount = 0
+ def totalBytes = 0
+}
+totals = [:]
+LOGFILE.trim().split('\n').each{ line ->
+ m = line =~ regex
+ if (m.matches()) {
+ date = m[0][fields.indexOf('date')+1]
+ s = totals.get(date, new Summary())
+ s.accessCount++
+ if (m[0][fields.indexOf('method')+1] == 'POST') s.postCount++
+ s.totalBytes += (m[0][fields.indexOf('bytes')+1]).toInteger()
+ def url = m[0][fields.indexOf('url')+1]
+ if (url == '/') s.homeCount++
+ s.what[url] = s.what.get(url, 0) + 1
+ def host = m[0][fields.indexOf('client')+1]
+ s.hosts[host] = s.hosts.get(host, 0) + 1
+ }
+}
+report('Date','Hosts','Accesses','Unidocs','POST','Home','Bytes')
+totals.each{ key, s ->
+ report(key, s.hosts.size(), s.accessCount, s.what.size(), s.postCount, s.homeCount, s.totalBytes)
+}
+v = totals.values()
+report('Grand Total', v.sum{it.hosts.size()}, v.sum{it.accessCount}, v.sum{it.what.size()},
+ v.sum{it.postCount}, v.sum{it.homeCount}, v.sum{it.totalBytes} )
+
+def report(a, b, c, d, e, f, g) {
+ printf ("%12s %6s %8s %8s %8s %8s %10s\n", [a,b,c,d,e,f,g])
+}
+// =>
+// Date Hosts Accesses Unidocs POST Home Bytes
+// 03/Jul/1996 1 1 1 1 0 5593
+// 10/Oct/2000 1 1 1 0 0 2326
+// 04/Sep/2005 1 2 2 0 1 2230
+// 05/Sep/2005 1 2 1 0 0 12456
+// 26/Apr/2000 3 6 6 0 0 1579790
+// 27/Apr/2000 1 3 3 0 0 5349
+// Grand Total 8 15 14 1 1 1607744
+
+
+// Some open source log processing packages in Java:
+// http://www.generationjava.com/projects/logview/index.shtml
+// http://ostermiller.org/webalizer/
+// http://jxla.nvdcms.org/en/index.xml
+// http://polliwog.sourceforge.net/index.html
+// as well as textual reports, most of these can produce graphical reports
+// Most have their own configuration information and Java extension points.
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.14
+//----------------------------------------------------------------------------------
+ import org.cyberneko.html.filters.Writer
+ import org.cyberneko.html.filters.DefaultFilter
+ import org.apache.xerces.xni.parser.XMLDocumentFilter
+ import org.apache.xerces.xni.*
+ import org.cyberneko.html.parsers.DOMParser
+ import org.xml.sax.InputSource
+
+ input = '''
+ <HTML><HEAD><TITLE>Hi!</TITLE></HEAD><BODY>
+ <H1>Welcome to Scooby World!</H1>
+ I have <A HREF="pictures.html">pictures</A> of the crazy dog
+ himself. Here's one!<P>
+ <IMG SRC="scooby.jpg" ALT="Good doggy!"><P>
+ <BLINK>He's my hero!</BLINK> I would like to meet him some day,
+ and get my picture taken with him.<P>
+ P.S. I am deathly ill. <A HREF="shergold.html">Please send
+ cards</A>.
+ </BODY></HTML>
+ '''
+
+ class WordReplaceFilter extends DefaultFilter {
+ private before, after
+ WordReplaceFilter(b, a) { before = b; after = a }
+ void characters(XMLString text, Augmentations augs) {
+ char[] c = text.toString().replaceAll(before, after)
+ super.characters(new XMLString(c, 0, c.size()), augs)
+ }
+ void setProperty(String s, Object o){}
+ }
+ XMLDocumentFilter[] filters = [
+ new WordReplaceFilter(/(?sm)picture/, /photo/),
+ new Writer()
+ ]
+ parser = new DOMParser()
+ parser.setProperty("http://cyberneko.org/html/properties/filters", filters)
+ parser.parse(new InputSource(new StringReader(input)))
+//----------------------------------------------------------------------------------
+
+
+// @@PLEAC@@_20.15
+//----------------------------------------------------------------------------------
+import org.cyberneko.html.filters.Writer
+import org.cyberneko.html.filters.DefaultFilter
+import org.apache.xerces.xni.parser.XMLDocumentFilter
+import org.apache.xerces.xni.*
+import org.cyberneko.html.parsers.DOMParser
+import org.xml.sax.InputSource
+
+input = '''
+<HTML><HEAD><TITLE>Hi!</TITLE></HEAD><BODY>
+<H1>Welcome to Scooby World!</H1>
+I have <A HREF="pictures.html">pictures</A> of the crazy dog
+himself. Here's one!<P>
+<IMG SRC="scooby.jpg" ALT="Good doggy!"><P>
+<BLINK>He's my hero!</BLINK> I would like to meet him some day,
+and get my picture taken with him.<P>
+P.S. I am deathly ill. <A HREF="shergold.html">Please send
+cards</A>.
+</BODY></HTML>
+'''
+
+class HrefReplaceFilter extends DefaultFilter {
+ private before, after
+ HrefReplaceFilter(b, a) { before = b; after = a }
+ void startElement(QName element, XMLAttributes attributes, Augmentations augs) {
+ def idx = attributes.getIndex('href')
+ if (idx != -1) {
+ def newtext = attributes.getValue(idx).replaceAll(before, after)
+ attributes.setValue(idx, URLEncoder.encode(newtext))
+ }
+ super.startElement(element, attributes, augs)
+ }
+ void setProperty(String s, Object o){}
+}
+XMLDocumentFilter[] myfilters = [
+ new HrefReplaceFilter(/shergold.html/, /cards.html/),
+ new Writer()
+]
+parser = new DOMParser()
+parser.setProperty("http://cyberneko.org/html/properties/filters", myfilters)
+parser.parse(new InputSource(new StringReader(input)))
+//----------------------------------------------------------------------------------
+
diff --git a/test/scanners/groovy/suite.rb b/test/scanners/groovy/suite.rb
new file mode 100644
index 0000000..6f20e6a
--- /dev/null
+++ b/test/scanners/groovy/suite.rb
@@ -0,0 +1,4 @@
+module ScannerTests
+ class Groovy < CodeRay::TestCase
+ end
+end \ No newline at end of file