How Groovy Helps

87
How Groovy Helps Ken Kousen Kousen IT, Inc. http://www.kousenit.com [email protected] http://kousenit.wordpress.com

description

An introduction to Groovy for Java developers, showing how a few small problems were solved rather than presenting an exhaustive list of features.

Transcript of How Groovy Helps

Page 1: How Groovy Helps

How Groovy Helps

Ken KousenKousen IT, Inc.

http://www.kousenit.com [email protected]

http://kousenit.wordpress.com

Page 2: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 2

What is Groovy? From Scott Davis, Groovy Recipes:

Groovy is what Java would look like had it been written in the 21st century.

Groovy is a new breed of language. It doesn’t replace old technology as much as enhances it … this isn’t a “Hey guys, let’s rewrite our application from the ground up” … this is a “Let’s use a language that seamlessly integrates with our existing codebase” approach

Page 3: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 3

Groovy Programming language with new features

Optional typing Closures Builders

Compiles to the JVM Compiled, not interpreted Interacts transparently with Java

If you don’t know the Groovy way, use Java Scripts as well as classes

Open source project

Page 4: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 4

Groovy Features

Additions and simplifications to Java Special features of its own

Lists and Maps as part of the language Everything is an object Metaprogramming makes it easy to

create Domain Specific Languages Like Grails

Page 5: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 5

Installing Groovy

Need a JVM first JDK 1.5+ preferred, others still work

Download Groovy from http://groovy.codehaus.org

Run installer (Windows) or just unzip and set GROOVY_HOME variable Add the bin directory to your path That’s all there is to it

Page 6: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 6

Groovy Editors

Groovy comes with groovysh command line client groovyconsole Swing GUI

Plug-ins for most major IDEs Eclipse Netbeans JetGroovy plugin for IntelliJ IDEA

Favored by most of the core team members

Page 7: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 7

Three Sample Problems

Google Chart is a web service that generates plots based on encoded data RESTful web service

Major League Baseball provides game information online XML processing and networking

Analyze training days data Database access, closures, and ranges

Page 8: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 8

Google Chart API

The Google Chart API is a web service located at http://code.google.com/apis/chart/

So-called “RESTful” web service URL with query string to generate

response Lots of different chart types can be

generated Data must be “encoded”

Page 9: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 9

Google Chart API Three types of encoding

Simple encoding uses alphanumeric characters A to Z, a to z, 0 to 9

62 unique values Text encoding uses floating point numbers

0.0 to 100.0 with a single decimal place 1000 unique values

Extended encoding uses pairs of alphanumeric characters

4096 unique values

Page 10: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 10

Google Chart API Typical request:

http://chart.apis.google.com/chart?chs=200x125&chd=s:helloWorld&cht=lc&chxt=x,y&chxl=0:|Mar|Apr|May|June|July|1:||50+Kb

Page 11: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 11

My Application Create pie chart of categories of training

classes delivered in 2007 To do so

Iterate over list of courses For each course, choose a category and

increment the number of courses in that category Encode the map into a string required by Google

Chart Create the required URL and access Google Chart

Page 12: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 12

My Application More important, illustrate various

aspects of Groovy programming Groovy scripts and classes Lists and maps as native data structures Regular expressions Groovy Strings Ranges URL creation and access Groovy bean properties

Page 13: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 13

Groovy Scripts and Classes

Groovy uses classes just as Java does Classes assumed to be public

Groovy also uses scripts Scripts become classes internally

Can mix script code and classes in the same source code file

Page 14: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 14

google_chart.groovypackage chart

class Chart { def base = 'http://chart.apis.google.com/chart' def url = base + '?cht=p3&chs=500x150' def imageTag

// … more code to come …}

def chart = new Chart()chart.encodeMap()println chart.imageTag

Page 15: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 15

Interesting Features No semicolons!

Actually, they’re optional Groovy code organized in packages Class is public by default

Fields are private by default Methods are public by default

Optional data typing def means untyped Can declare a type if you want

Page 16: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 16

Interesting Features Strings can use either single or

double quotes Single quotes imply regular Strings Double quotes are “GStrings” and can

do variable replacement Script code shown in same file Accesses the imageTag property

Define a property, automatically get a public getter and a public setter method

Page 17: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 17

More Fieldsdef data = [ 'Spring and Hibernate', 'EJB', 'J2EE', 'J2EE', 'Spring', 'XML', 'Spring', 'Spring', 'XML', 'XML', 'J2EE', 'WS', 'Struts', 'Spring', 'JS', 'WS', 'Ajax', 'WS', 'Portlets', 'Hibernate and EJB3', 'J2EE', 'OOAD', 'Java', 'Ajax', 'Ajax', 'Spring', 'Struts 2', 'XML and Java', 'Ajax and Java', 'Securing WS', 'JSF', 'Ajax', 'Ajax and Java', 'Ajax', 'J2EE', 'WS']

def map = [:]

Page 18: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 18

Lists and Maps

Lists and Maps are native data structures

Lists declared using array syntax Default implementation is an ArrayList

Maps are defined using [key:value] Default implementation is a HashMap

Page 19: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 19

Constructor CodeChart() { data.each { name -> if (name =~ (/Spring|Hibernate|Struts/)) { addToMap('Open Source') } if (name =~ (/Ajax|JS/)) { addToMap('Ajax') }

// … other categories … }}

Page 20: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 20

Constructor Code List’s each method takes a Closure as an

argument Each element of the list is passed into the

closure as the variable name If closure is the last argument of a

method, can be placed after the method Closure is a block of code and the context

in which it runs Can be assigned to variables, passed into

methods, and so on

Page 21: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 21

Constructor Code

Regular expression declared inside forward slashes Known as “slashy” syntax Any string can be defined that way

The =~ is the “find” operator Operator ==~ is the “match”

operator Operator ~ is the pattern operator

Page 22: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 22

addToMap(label)

def addToMap(label) { map[label] = map.get(label,0) + 1}

Page 23: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 23

addToMap(label) Method signature uses def as return type

Can return a value, or not Dummy argument doesn’t require a type

definition get(label,0)

Search for the value under the key label Return it if it exists, assign it with the default

value 0 if not Either way, increment the value and assign it

back into the map under the label key Can access a value using get method or

using array syntax map[label]

Page 24: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 24

addToMap(label)

Resulting map of categories is [“Open Source”:9, “J2EE”:12, “Web Services”:9, “Ajax”:9, “Other”:1]

(some courses appear in more than one category)

Page 25: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 25

Encoding Data values are all integers less than 64

Can always scale the data if not Use the Google Chart simple encoding

Values from 0 to 25 are the letters A to Z Values from 26 to 51 are the letters a to z Values from 52 to 61 are the numbers 0 to 9

Encode each value, then concatenate the results into a string which will be added as a query parameter

Page 26: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 26

encodeMap()

def encodeMap() { List list = (('A'..'Z')+('a'..'z')+(0..9)) as List

String s = "&chd=s:" List keys = [] map.each { k,v -> keys << k s += list[v] }

// … more to come …

Page 27: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 27

encodeMap() Values separated by two dots are Ranges Each range concatenated together The as operator “coerces” the Range into a List Could also use the toList() method

The each method iterates over map entries and passes each to the closure Closure declares keys as k, values as v Each k is appended to the keys list, using the <<

operator Values are used as an index to the range list,

which returns the proper encoded value

Page 28: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 28

encodeMap()

String labels = "&chl=" + keys.collect { URLEncoder.encode(it,"UTF-8") }.join('|')

url += s // the query string, defined in the code aboveurl += labelsprintln url // might as well see itString urle = url.replaceAll(/&/,'&amp;')imageTag = "<img src='${urle}' alt='Course distribution'/>"

Page 29: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 29

encodeMap() Lots going on here Need to get the labels for the pie chart

The labels will be the keys from the map Encoding requires the labels to be

concatenated into a single string using | as a separator

The collect method passes each element of a collection through the closure Since no dummy variable was defined for the

closure this time, the default value “it” is used The src attribute set as a GString between ${…}

Page 30: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 30

encodeMap() Each label is passed through the standard Java URLEncoder.encode() method

If you don’t have a specifically Groovy way to do something, use Java

Total URL is fine, but if it is the argument to the src attribute of an img tag, it can’t include &

Have to encode & using the entity reference &amp; The replaceAll method replaces each string that

matches the regular expression with the supplied value

Page 31: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 31

Result <img src='http://chart.apis.google.com/chart?cht=p3

&amp;chs=500x150&amp;chd=s:JMJJB&amp;chl=Open+Source|J2EE|Web+Services|Ajax|

Other' alt='Course distribution'/> The chart type is p3 (a 3D pie chart) The chart size (chs) is 500x150 pixels The encoded chart data was JMJJB (9, 12, 9, 9, 1)

The s: indicates the simple encoding scheme The chart labels are in the URL encoded string after chl

Page 32: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 32

Result

Page 33: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 33

Google Chart Builder Zan Thrash has created a full builder called

the Google Chart Builder See his blog posting at

http://zanthrash.blogspot.com/2008/01/groovy-chart-builder.html

Source code is available and instructive Grails has a Google Chart plugin at

http://grails.org/Google+Chart+Plugin Either of those are excellent for real work

Page 34: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 34

Baseball Data Publicly accessible in XML format Base URL is

http://gd2.mlb.com/components/game/mlb Games are filed by year, month, and day Game directory uses team abbreviations

2007 World Series Game 4: year_2007/month_10/day_28/gid_2007_10_28_bosmlb_colmlb_1/boxscore.xml

Page 35: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 35

boxscore.xml Typical boxscore includes

Root element is <boxscore> Child elements

<linescore> <pitching> for home and away teams

<pitcher> element for each pitcher <batting> for home and away teams

<batter> element for each batter Assorted <note>, <text_data>, <game_info> elements

Page 36: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 36

Java Approach Consider the Java code to access the

data Open a URL and a URLConnection Get a DocumentBuilderFactory Get a DocumentBuilder Invoke the parse method using an InputStream from the URLConnection

Either traverse the tree or call getElementByTagName to retrieve the data

Don’t forget all the try/catch blocks

Page 37: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 37

Groovy Approach

Use XmlParser or XmlSlurper Very similar classes with mostly the

same methods Parser returns a list of nodes to navigate Slurper returns GPathResults, which are

like XPath but groovier Just to keep things simple, since the

data isn’t massive, we’ll use the parser

Page 38: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 38

GetGameData.groovy

class GetGameData { def day def month def year def base = 'http://gd2.mlb.com/components/game/mlb/'

// all teams in actual file def abbrevs = [ ana:"Los Angeles (A)",ari:"Arizona",atl:"Atlanta", bal:"Baltimore",bos:"Boston",cha:"Chicago (A)", chn:"Chicago (N)",cin:"Cincinnati",cle:"Cleveland", col:"Colorado", was:"Washington"]

Page 39: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 39

getGame()

void getGame(away, home, num) { println "$away at $home on ${month}/${day}/${year}" def url = base + "year_${year}/month_${month}/day_${day}/" def game = "gid_${year}_${month}_${day}_" + "${away}mlb_${home}mlb_${num}/boxscore.xml" def boxscore = new XmlParser().parse(url + game)

Page 40: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 40

getGame()

def awayName = boxscore.'@away_fname'def awayScore = boxscore.linescore[0].'@away_team_runs'def homeName = boxscore.'@home_fname'def homeScore = boxscore.linescore[0].'@home_team_runs'println "$awayName $awayScore, $homeName $homeScore (game $num)"

Page 41: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 41

getGame()

def pitchers = boxscore.pitching.pitcher pitchers.each { p -> if (p.'@note' && p.'@note' =~ /W|L|S/) { println " ${p.'@name'} ${p.'@note'}" } }

Page 42: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 42

Interesting Features GStrings simplify string handling

Java would have to use concatenation to mix strings and variables

XmlParser has a parse method that takes a URL

Result is a DOM tree Child elements found using dot notation

boxscore.pitching.pitcher selects all pitchers Attribute values use @ notation

Page 43: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 43

Result (so far)

bos at col on 10/28/2007Boston Red Sox 4, Colorado Rockies 3 (game 1) Lester (W, 1-0) Papelbon (S, 3) Cook (L, 0-1)

Page 44: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 44

Process the Data

def batters = boxscore.batting.batterfor (b in batters) { println "${b.'@name'} went ${b.'@h'} for ${b.'@ab'}"}println batters.size() + " total batters"println 'Total hits: ' + batters.'@h'*.toInteger().sum()

Page 45: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 45

Notes

Groovy prefers for/in loop Could use batters.each here instead

NodeList has size() method So does List, String, arrays, etc.

Spread-dot operator, *. Applies function after dot to each

element of a list

Page 46: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 46

More Processing

println "Batters with at least one hit:" println batters.findAll { it.'@h'.toInteger() > 0 }.collect { it.'@name' + '(' + it.'@h' + ')' }}

Page 47: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 47

List Processing List has a findAll method that takes a

closure Default reference to each element is “it” List also has collect method

Applies closure to each element and returns a list containing the results

Many other list processing methods available each, every, unique, sort, join, flatten, reverse, intersect, +, -

Page 48: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 48

XML Builder

Groovy implements Builder pattern Uses metaprogramming to intercept

“pseudo” method calls Builders already included

NodeBuilder for XML AntBuilder for creating Ant scripts SwingBuilder for GUIs BuilderSupport for creating your own

Page 49: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 49

Create HTML

void writeHtml(node) { def away = node.'@away_sname‘ def home = node.'@home_sname' def name = "$away at $home" new File("${away}${home}.html").withWriter { w -> def builder = new MarkupBuilder(w)

Page 50: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 50

Notes

File has withWriter method that creates a Writer and automatically closes it when done

MarkupBuilder overloaded to take a Writer Default writes to standard out

Page 51: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 51

Building HTML

builder.html { head { title name } body { h1 name h2 node.'@date' node.batting.each { batters -> def team = batters.'@team_flag' + '_sname' h2 node."@${team}"

Page 52: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 52

Notes

Looks like HTML Each HTML term is intercepted

Builder then creates XML node with that name

Closure after node indicates child node Argument in parentheses is attribute

Can freely mix regular Groovy variables and the builder nodes

Page 53: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 53

More HTML

table (border:'2') { tr { th 'Batter'; th 'AB'; th 'R'; th 'H' th 'RBI'; th 'BB'; th 'SO' } batters.batter.findAll { it.'@pos' != 'P' }.each { b -> tr { td b.'@name' + ' (' + b.'@pos' + ')' td b.'@ab'; td b.'@r'; td b.'@h' td b.'@rbi'; td b.'@bb'; td b.'@so' } }

Page 54: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 54

Table, continued

tr { td(style:'font-weight:bold','Totals') td batters.batter.'@ab'*.toInteger().sum() td batters.batter.'@r'*.toInteger().sum() td batters.batter.'@h'*.toInteger().sum() td batters.batter.'@rbi'*.toInteger().sum() td batters.batter.'@bb'*.toInteger().sum() td batters.batter.'@so'*.toInteger().sum() }}

Page 55: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 55

Results

HTML output file Generating XML just as easy Swing GUIs are fun, too

Page 56: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 56

HTML Boxscore

Page 57: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 57

Screen Scraping

Want all games for a given date MLB web page lists games in

subdirectories, as discussed above Can’t just use XML Parser

HTML not necessarily well-formed Without a schedule, don’t know which

games played on each day Grab page and screen scrape

Page 58: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 58

Games for 5/5/2007

Page 59: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 59

Find Games on Date

void getGames() { println "Games for ${month}/${day}/${year}" def url = base + "year_${year}/month_${month}/day_${day}/" def gamePage = new URL(url).text def pattern = /\"gid_${year}_${month}_${day}_(\w*)mlb_(\w*)mlb_(\d)/

Page 60: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 60

Notes URL class has getText() method

Invoked using text property Retrieves entire page for processing

Could use eachLine() method instead Processes line by line Available in any File Works for URLs, too, by duck typing

Each game in a link with the above pattern Also in text, so include the leading quotes to

distinguish between the two Groups (\w*) contain the team abbreviations

Page 61: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 61

Process HTML Pagedef m = gamePage =~ patternif (m) { (0..<m.count).eachWithIndex { line, i -> def away = m[line][1] def home = m[line][2] def num = m[line][3]

try { getGame(away,home,num) } catch (Exception e) { println abbrevs[away] + " at " + abbrevs[home] + " not started yet"} } }

Page 62: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 62

Notes

Matcher’s count property tells how many matches

Range (0..<x) is from 0 up to but not including x

Iterator eachWithIndex available Index not actually used here, just

shown for illustration

Page 63: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 63

ResultsGames for 05/05/2007Boston at Minnesota on 05/05/2007Boston Red Sox 1, Minnesota Twins 2 (game 1) Tavarez (L, 1-3) Santana (W, 4-2) Nathan (S, 8)Chicago (A) at Los Angeles (A) on 05/05/2007Chicago White Sox 6, Los Angeles Angels 3 (game 1) Garland (W, 1-2) Jenks (S, 9) Lackey (L, 4-3)…

Page 64: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 64

Database Data

Groovy simplifies database access groovy.sql.Sql class

Can do inserts, updates, deletes, and queries

Includes many convenience methods

Page 65: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 65

Training Data Sample data from training company

Want to determine teaching days and revenue Three tables

Location id, city, state, version (for optimistic locking) latitude, longitude (computed from web service)

Client id, name, version

Course id, title, start_date, end_date, rate, version

location_id FK to Location.id client_id FK to Client.id

Page 66: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 66

Training Data Data stored in MySQL database

Use MySQL JDBC driver Note length of course is dependent

variable Computed from start_date and end_date

Revenue adding in rate for each day, since course may have calendar gaps

Lots of annoying Java calendar processing

Page 67: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 67

Side Issues Initial insert into database done by

collecting data in spreadsheet Can access spreadsheet directly using

Scriptom library Alternative is to save as CSV and process

file Need latitude and longitude for each

city, state in order to create Google Map

Page 68: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 68

Sample CSV Data

75,5/14/2007,5/18/2007,Java Web Services,Lancaster,PA76,5/21/2007,5/25/2007,Ajax and Struts,Philadelphia,PA77,6/4/2007,6/12/2007,Java Web Services,McLean,VA78,6/25/2007,6/29/2007,Portal Dev. Using RAD6,Enola,PA80,7/9/2007,7/14/2007,Hibernate EJB3,Atlanta,GA81,8/23/2007,8/23/2007,J2EE for Managers,Marlborough,CT

Page 69: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 69

Process CSV Data

class Record { int num; String startDate; String endDate String title; String city; String state

Record(String[] s) { num = s[0].startsWith("#") ? 0 : s[0].toInteger() startDate = s[1]; endDate = s[2] title = s[3]; city = s[4]; state = s[6] } String toString() { "$title ($startDate -- $endDate) $city, $state"} }

Page 70: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 70

Process CSV Datadef records = []try { def dataFile = new File('Course_locations.csv') def lines = dataFile.readLines() for (line in lines) { elements = line.trim().split(',') records << new Record(*elements) }} catch (FileNotFoundException e) { println e}records.each { println it }

Page 71: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 71

GroovyBeans

GroovyBeans declare properties Put in fields, accessors automatically

generated (Groovy calls both gets and sets

accessors) Map constructor generated, too

new Record(title:”Groovy”,…) assigns value to title, etc.

Page 72: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 72

Notes File has readLines method

Closure argument applies to each line Can use String methods like split and trim

Spread operator takes list and splits it into input arguments Again, not really necessary here since

constructor takes an array, but interesting nevertheless

Note toString method does not require a return keyword Value of closure returned by default

Page 73: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 73

Geocoding

Geocoders available as web services Google has one So does the USGS

Return information as XML or CSV

Page 74: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 74

Using Google Geocoderdef key = ‘long_ugly_key'def base = 'http://maps.google.com/maps/geo'def city = 'Marlborough'def state = 'CT'

def query = "q=${city},+${state}&output=csv&key=${key}"def url_string = base + '?q=' + ',+' + URLEncoder.encode(city,"UTF-8") + ',+' + state + "&output=csv&key=${key}"def results = new URL(url_string).text.split(',')def latitude = results[-2].toDouble();def longitude = results[-1].toDouble();println "(${latitude},${longitude})"assert "(41.63256,-72.46315) == (${latitude},${longitude})"

Page 75: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 75

Google Geocoder

When csv parameter included, result is four strings HTTP status code (hopefully 200) Accuracy (magnification level) Latitude Longitude

Note use in script of indices -1, -2 to grab two elements from the end

Page 76: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 76

USGS Geocoder

def partialCsvRequest = "http://geocoder.us/service/csv/geocode?address="def address = "11 Emily Road, Marlborough, CT"def csvUrl = new URL(partialCsvRequest + URLEncoder.encode(address))def csvResponse = csvUrl.textdef tokens = csvResponse.split("," )println "Latitude: [${tokens[0]}]"println "Longitude: [${tokens[1]}]"println "Address: [${tokens[2]}]"println "City: [${tokens[3]}]"println "State: [${tokens[4]}]"println "Zip: [${tokens[5]}]"

Page 77: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 77

TrainingCourse

class TrainingCourse { String title; Date startDate; Date endDate; double rate

String toString() { def nf = NumberFormat.currencyInstance "(${title},(${startDate} - ${endDate}),${nf.format(rate)})" }

int getLength() { (startDate..endDate).size() }

double getRevenue() { length * rate }}

Page 78: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 78

Notes

Can provide get methods as necessary

Dates are a range now..then size() returns number of days Can add, subtract days

Page 79: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 79

CourseProcessor.groovyclass CourseProcessor { def courses = [] def courseYearMap = new TreeMap() def cal = Calendar.instance

CourseProcessor() { def USER = ‘xxx‘; def PASS = ‘xxx’; def URL = ‘xxx‘; def DRIVER = ‘xxx' def conn = Sql.newInstance(URL,USER,PASS,DRIVER) conn.eachRow('select * from course') { row -> courses << new TrainingCourse( title:row.title,startDate:row.start_date, endDate:row.end_date,rate:row.rate) }

Page 80: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 80

Continuing…

courses.each { c -> cal.time = c.startDate def year = cal.get(Calendar.YEAR) courseYearMap[year] = courseYearMap.get(year,[]) + c }}

Page 81: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 81

Notes

Want to sort courses by year Current approach is to add them to a

map Using TreeMap instead of [:] means

courses automatically sorted by key on add

Map contains Years as keys List of courses as values

Page 82: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 82

Printing Courses

def printYearMap() { def nf = NumberFormat.currencyInstance courseYearMap.each { year,courseList -> def monthlyDays = new int[12] def monthlyRev = new double[12] courseList.each { course -> cal.time = course.startDate def month = cal.get(Calendar.MONTH) for (date in course.startDate..course.endDate) { monthlyDays[month] += 1 monthlyRev[month] += course.rate } }

Page 83: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 83

Continuing

def days = (courseList*.length).sum()def rev = (courseList*.revenue).sum()println "Totals for $year : ${days} days, ${nf.format(rev)}"

Page 84: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 84

Testing Testing

assert keyword used everywhere GroovyTestCase extends JUnit TestCase

assertArrayEquals assertContains(int, int[]) assertLength assertScript(String script)

Asserts script runs without exceptions shouldFail(Closure) assert closure fails when

run

Page 85: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 85

Testing

Can execute any GroovyTestCase as regular Groovy script Runs as though it has main method

Mocks and Stubs built in, too

Page 86: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 86

Miscellaneous I see I’ve left out lots of cool topics

Safe dereferencing operator ?. a?.b?.c checks for null a and b

Templates look like JSPs, but run with Groovy scripts Will discuss with Grails

Metaprogramming (the red pill) Great for creating DSLs

Heredocs def doc = '''This is

a multiline script written over several lines''‘

Grails!

Page 87: How Groovy Helps

04/12/23 (c) 2008 by Kousen IT, Inc. 87

References

Groovy home is http://groovy.codehaus.org Lots of info, plug-ins, details Mailing lists at

http://groovy.codehaus.org/Mailing+Lists News portal at http://aboutgroovy.com Lots of samples at

http://pleac.sourceforge.net/pleac_groovy/