diff options
author | murphy <murphy@rubychan.de> | 2009-01-13 16:00:19 +0000 |
---|---|---|
committer | murphy <murphy@rubychan.de> | 2009-01-13 16:00:19 +0000 |
commit | dee392f22511b9e91dfa3dc089be30a940e027c9 (patch) | |
tree | 937f2dc237605467ae17d65149a5ee1f672cc600 | |
parent | 0aa5f6d83c6d2bb0b4b5df45cedf057cd9c5099e (diff) | |
download | coderay-dee392f22511b9e91dfa3dc089be30a940e027c9.tar.gz |
New: *Groovy Scanner* (feature #60)
* It inherits from Java Scanner, re-using some constants.
* Thanks to Stefan Weinmann for helping me with Groovy.
* PLEAC code used for testing.
* Some issues with remain with strings and regexps.
* Rating: 2 / 5 stars, "Basic support"
* Added .groovy and .gvy to FileType.
* CodeRay now supports a language that pygments doesn't support ;-)
-rw-r--r-- | lib/coderay/helpers/file_type.rb | 2 | ||||
-rw-r--r-- | lib/coderay/scanners/groovy.rb | 247 | ||||
-rw-r--r-- | test/scanners/groovy/pleac.expected.raydebug | 10953 | ||||
-rw-r--r-- | test/scanners/groovy/pleac.in.groovy | 10953 | ||||
-rw-r--r-- | test/scanners/groovy/suite.rb | 4 |
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.®ister) + 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 |